mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: add support for additional filter/mapping (#2609)
- comprises a few different elements: FilterField (currently packaged in PatchFunction1Types namespace) ~~~~~~~~~~~ The FilterField helper class provides a multi-sweep median filter for a Field of data associated with a geometric point cloud. The points can be freestanding or the faceCentres (or points) of a meshedSurface, for example. Using an initial specified search radius, the nearest point neighbours are gathered and addressing/weights are built for them. This currently uses an area-weighted, linear RBF interpolator with provision for quadratic RBF interpolator etc. After the weights and addressing are established, the evaluate() method can be called to apply a median filter to data fields, with a specified number of sweeps. boundaryDataSurfaceReader ~~~~~~~~~~~~~~~~~~~~~~~~~ - a surfaceReader (similar to ensightSurfaceReader) when a general point data reader is needed. MappedFile ~~~~~~~~~~ - has been extended to support alternative surface reading formats. This allows, for example, sampled ensight data to be reused for mapping. Cavaet: multi-patch entries may still needs some work. - additional multi-sweep median filtering of the input data. This can be used to remove higher spatial frequencies when sampling onto a coarse mesh. smoothSurfaceData ~~~~~~~~~~~~~~~~~ - standalone application for testing of filter radii/sweeps
This commit is contained in:
@ -0,0 +1,3 @@
|
|||||||
|
smoothSurfaceData.C
|
||||||
|
|
||||||
|
EXE = $(FOAM_USER_APPBIN)/smoothSurfaceData
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
/* Can compile with -fopenmp etc */
|
||||||
|
|
||||||
|
EXE_INC = \
|
||||||
|
-I$(LIB_SRC)/fileFormats/lnInclude \
|
||||||
|
-I$(LIB_SRC)/surfMesh/lnInclude \
|
||||||
|
-I$(LIB_SRC)/meshTools/lnInclude
|
||||||
|
|
||||||
|
EXE_LIBS = \
|
||||||
|
-lfileFormats \
|
||||||
|
-lsurfMesh \
|
||||||
|
-lmeshTools
|
||||||
@ -0,0 +1,180 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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/>.
|
||||||
|
|
||||||
|
Application
|
||||||
|
smoothSurfaceData
|
||||||
|
|
||||||
|
Description
|
||||||
|
Pre-processing, filtering of surface field data.
|
||||||
|
Currently this is just used to test filter settings
|
||||||
|
(for MappedFile) and only generates vtk output, which can be
|
||||||
|
easily loaded in ParaView.
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "argList.H"
|
||||||
|
#include "Time.H"
|
||||||
|
#include "clockTime.H"
|
||||||
|
#include "primitiveFields.H"
|
||||||
|
#include "surfaceReader.H"
|
||||||
|
#include "surfaceWriter.H"
|
||||||
|
#include "foamVtkSurfaceWriter.H"
|
||||||
|
#include "MappedFileFilterField.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
using namespace Foam;
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
argList::addNote
|
||||||
|
(
|
||||||
|
"Testing, pre-processing, filtering of surface field data"
|
||||||
|
);
|
||||||
|
|
||||||
|
argList::noCheckProcessorDirectories();
|
||||||
|
argList::addVerboseOption("Additional verbosity");
|
||||||
|
|
||||||
|
argList::addArgument("input", "The input surface file");
|
||||||
|
|
||||||
|
argList::addOption("radius", "m", "Specify filter radius [metres]");
|
||||||
|
argList::addOption("sweeps", "N", "Number of median filter stages");
|
||||||
|
argList::addOption
|
||||||
|
(
|
||||||
|
"field",
|
||||||
|
"name",
|
||||||
|
"Field <scalar> to process (default: T)"
|
||||||
|
);
|
||||||
|
|
||||||
|
argList::addOption
|
||||||
|
(
|
||||||
|
"read-format",
|
||||||
|
"type",
|
||||||
|
"Input format (default: ensight)"
|
||||||
|
);
|
||||||
|
|
||||||
|
argList args(argc, argv);
|
||||||
|
// #include "setRootCase.H"
|
||||||
|
|
||||||
|
const int optVerbose = args.verbose();
|
||||||
|
|
||||||
|
const word readFileType
|
||||||
|
(
|
||||||
|
args.getOrDefault<word>("read-format", "ensight")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Constant radius searching
|
||||||
|
label filterSweeps_(1);
|
||||||
|
scalar filterRadius_(0);
|
||||||
|
args.readIfPresent("sweeps", filterSweeps_);
|
||||||
|
args.readIfPresent("radius", filterRadius_);
|
||||||
|
|
||||||
|
Info<< nl
|
||||||
|
<< "Filter: radius=" << filterRadius_
|
||||||
|
<< " sweeps=" << filterSweeps_ << endl;
|
||||||
|
|
||||||
|
// Simple sanity check
|
||||||
|
if ((filterSweeps_ < 1) || (filterRadius_ <= VSMALL))
|
||||||
|
{
|
||||||
|
Info<< nl << "Nothing to do. Exiting..." << nl << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
word fieldName("T");
|
||||||
|
args.readIfPresent("field", fieldName);
|
||||||
|
|
||||||
|
|
||||||
|
const fileName importName = args.get<fileName>(1);
|
||||||
|
|
||||||
|
auto readerPtr_ = surfaceReader::New(readFileType, importName);
|
||||||
|
|
||||||
|
auto& reader = readerPtr_();
|
||||||
|
|
||||||
|
const label fieldIndex = reader.fieldNames(0).find(fieldName);
|
||||||
|
if (fieldIndex == -1)
|
||||||
|
{
|
||||||
|
FatalErrorInFunction
|
||||||
|
<< "Unable to find field name: " << fieldName
|
||||||
|
<< " in list of available fields: " << reader.fieldNames(0)
|
||||||
|
<< exit(FatalError);
|
||||||
|
}
|
||||||
|
|
||||||
|
clockTime timing;
|
||||||
|
|
||||||
|
const meshedSurface& geom = reader.geometry(0);
|
||||||
|
|
||||||
|
Info<< nl << "Read " << geom.nFaces() << " faces and "
|
||||||
|
<< geom.nPoints() << " points in "
|
||||||
|
<< timing.timeIncrement() << "s" << endl;
|
||||||
|
|
||||||
|
PatchFunction1Types::FilterField fieldFilter;
|
||||||
|
|
||||||
|
PatchFunction1Types::FilterField::debug = optVerbose;
|
||||||
|
|
||||||
|
fieldFilter.reset(geom, filterRadius_);
|
||||||
|
|
||||||
|
Info<< nl << "Built weights/addressing "
|
||||||
|
<< timing.timeIncrement() << "s" << endl;
|
||||||
|
|
||||||
|
Info<< nl << "Processing " << reader.times().size() << " times" << nl;
|
||||||
|
|
||||||
|
const instantList times(reader.times());
|
||||||
|
|
||||||
|
forAll(times, timeIndex)
|
||||||
|
{
|
||||||
|
tmp<scalarField> tfield
|
||||||
|
(
|
||||||
|
reader.field(timeIndex, fieldIndex, pTraits<scalar>::zero)
|
||||||
|
);
|
||||||
|
|
||||||
|
tfield = fieldFilter.evaluate(tfield, filterSweeps_);
|
||||||
|
|
||||||
|
vtk::surfaceWriter writer
|
||||||
|
(
|
||||||
|
geom.points(),
|
||||||
|
geom,
|
||||||
|
word::printf("filtered_%06d", timeIndex)
|
||||||
|
);
|
||||||
|
|
||||||
|
writer.piece(geom.points(), geom);
|
||||||
|
|
||||||
|
writer.writeGeometry();
|
||||||
|
writer.beginCellData();
|
||||||
|
writer.writeCellData(fieldName, tfield());
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< nl << "Smoothing/writing "
|
||||||
|
<< timing.timeIncrement() << "s" << endl;
|
||||||
|
|
||||||
|
Info<< nl << "End\n" << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -327,6 +327,8 @@ PatchFunction1/makePatchFunction1s.C
|
|||||||
PatchFunction1/coordinateScaling/coordinateScalings.C
|
PatchFunction1/coordinateScaling/coordinateScalings.C
|
||||||
PatchFunction1/CodedField/makeCodedFields.C
|
PatchFunction1/CodedField/makeCodedFields.C
|
||||||
|
|
||||||
|
PatchFunction1/MappedFile/MappedFileFilterField.C
|
||||||
|
|
||||||
meshStructure/meshStructure.C
|
meshStructure/meshStructure.C
|
||||||
|
|
||||||
coupling/externalFileCoupler.C
|
coupling/externalFileCoupler.C
|
||||||
|
|||||||
@ -47,6 +47,12 @@ Foam::PatchFunction1Types::MappedFile<Type>::MappedFile
|
|||||||
fieldTableName_(dict.getOrDefault<word>("fieldTable", entryName)),
|
fieldTableName_(dict.getOrDefault<word>("fieldTable", entryName)),
|
||||||
pointsName_(dict.getOrDefault<word>("points", "points")),
|
pointsName_(dict.getOrDefault<word>("points", "points")),
|
||||||
mapMethod_(),
|
mapMethod_(),
|
||||||
|
filterRadius_(dict.getOrDefault<scalar>("filterRadius", 0)),
|
||||||
|
filterSweeps_(dict.getOrDefault<label>("filterSweeps", 0)),
|
||||||
|
filterFieldPtr_(nullptr),
|
||||||
|
readerFormat_(),
|
||||||
|
readerFile_(),
|
||||||
|
readerPtr_(nullptr),
|
||||||
mapperPtr_(nullptr),
|
mapperPtr_(nullptr),
|
||||||
sampleTimes_(),
|
sampleTimes_(),
|
||||||
sampleIndex_(-1, -1),
|
sampleIndex_(-1, -1),
|
||||||
@ -54,6 +60,40 @@ Foam::PatchFunction1Types::MappedFile<Type>::MappedFile
|
|||||||
sampleValues_(),
|
sampleValues_(),
|
||||||
offset_(Function1<Type>::NewIfPresent("offset", dict))
|
offset_(Function1<Type>::NewIfPresent("offset", dict))
|
||||||
{
|
{
|
||||||
|
// Simple sanity check
|
||||||
|
if ((filterSweeps_ < 1) || (filterRadius_ <= VSMALL))
|
||||||
|
{
|
||||||
|
filterRadius_ = 0;
|
||||||
|
filterSweeps_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict.readIfPresent("sampleFormat", readerFormat_))
|
||||||
|
{
|
||||||
|
dict.readEntry("sampleFile", readerFile_);
|
||||||
|
|
||||||
|
fileName fName(readerFile_);
|
||||||
|
fName.expand();
|
||||||
|
|
||||||
|
readerPtr_ = surfaceReader::New(readerFormat_, fName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
Info<< "mappedFile:" << nl;
|
||||||
|
if (readerFormat_.empty())
|
||||||
|
{
|
||||||
|
Info<< " boundary format" << nl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Info<< " format:" << readerFormat_
|
||||||
|
<< " file:" << readerFile_ << nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< " filter radius=" << filterRadius_
|
||||||
|
<< " sweeps=" << filterSweeps_ << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if
|
if
|
||||||
(
|
(
|
||||||
dict.readIfPresent("mapMethod", mapMethod_)
|
dict.readIfPresent("mapMethod", mapMethod_)
|
||||||
@ -88,6 +128,12 @@ Foam::PatchFunction1Types::MappedFile<Type>::MappedFile
|
|||||||
fieldTableName_(fieldTableName),
|
fieldTableName_(fieldTableName),
|
||||||
pointsName_(dict.getOrDefault<word>("points", "points")),
|
pointsName_(dict.getOrDefault<word>("points", "points")),
|
||||||
mapMethod_(),
|
mapMethod_(),
|
||||||
|
filterRadius_(dict.getOrDefault<scalar>("filterRadius", 0)),
|
||||||
|
filterSweeps_(dict.getOrDefault<label>("filterSweeps", 0)),
|
||||||
|
filterFieldPtr_(nullptr),
|
||||||
|
readerFormat_(),
|
||||||
|
readerFile_(),
|
||||||
|
readerPtr_(nullptr),
|
||||||
mapperPtr_(nullptr),
|
mapperPtr_(nullptr),
|
||||||
sampleTimes_(),
|
sampleTimes_(),
|
||||||
sampleIndex_(-1, -1),
|
sampleIndex_(-1, -1),
|
||||||
@ -95,6 +141,40 @@ Foam::PatchFunction1Types::MappedFile<Type>::MappedFile
|
|||||||
sampleValues_(),
|
sampleValues_(),
|
||||||
offset_(Function1<Type>::NewIfPresent("offset", dict))
|
offset_(Function1<Type>::NewIfPresent("offset", dict))
|
||||||
{
|
{
|
||||||
|
// Simple sanity check
|
||||||
|
if ((filterSweeps_ < 1) || (filterRadius_ <= VSMALL))
|
||||||
|
{
|
||||||
|
filterRadius_ = 0;
|
||||||
|
filterSweeps_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict.readIfPresent("sampleFormat", readerFormat_))
|
||||||
|
{
|
||||||
|
dict.readEntry("sampleFile", readerFile_);
|
||||||
|
|
||||||
|
fileName fName(readerFile_);
|
||||||
|
fName.expand();
|
||||||
|
|
||||||
|
readerPtr_ = surfaceReader::New(readerFormat_, fName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
Info<< "mappedFile:" << nl;
|
||||||
|
if (readerFormat_.empty())
|
||||||
|
{
|
||||||
|
Info<< " boundary format" << nl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Info<< " format:" << readerFormat_
|
||||||
|
<< " file:" << readerFile_ << nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< " filter radius=" << filterRadius_
|
||||||
|
<< " sweeps=" << filterSweeps_ << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if
|
if
|
||||||
(
|
(
|
||||||
dict.readIfPresent("mapMethod", mapMethod_)
|
dict.readIfPresent("mapMethod", mapMethod_)
|
||||||
@ -136,13 +216,27 @@ Foam::PatchFunction1Types::MappedFile<Type>::MappedFile
|
|||||||
fieldTableName_(rhs.fieldTableName_),
|
fieldTableName_(rhs.fieldTableName_),
|
||||||
pointsName_(rhs.pointsName_),
|
pointsName_(rhs.pointsName_),
|
||||||
mapMethod_(rhs.mapMethod_),
|
mapMethod_(rhs.mapMethod_),
|
||||||
|
filterRadius_(rhs.filterRadius_),
|
||||||
|
filterSweeps_(rhs.filterSweeps_),
|
||||||
|
filterFieldPtr_(nullptr),
|
||||||
|
readerFormat_(rhs.readerFormat_),
|
||||||
|
readerFile_(rhs.readerFile_),
|
||||||
|
readerPtr_(nullptr),
|
||||||
mapperPtr_(rhs.mapperPtr_.clone()),
|
mapperPtr_(rhs.mapperPtr_.clone()),
|
||||||
sampleTimes_(rhs.sampleTimes_),
|
sampleTimes_(rhs.sampleTimes_),
|
||||||
sampleIndex_(rhs.sampleIndex_),
|
sampleIndex_(rhs.sampleIndex_),
|
||||||
sampleAverage_(rhs.sampleAverage_),
|
sampleAverage_(rhs.sampleAverage_),
|
||||||
sampleValues_(rhs.sampleValues_),
|
sampleValues_(rhs.sampleValues_),
|
||||||
offset_(rhs.offset_.clone())
|
offset_(rhs.offset_.clone())
|
||||||
{}
|
{
|
||||||
|
if (!readerFormat_.empty() && !readerFile_.empty())
|
||||||
|
{
|
||||||
|
fileName fName(readerFile_);
|
||||||
|
fName.expand();
|
||||||
|
|
||||||
|
readerPtr_ = surfaceReader::New(readerFormat_, fName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
@ -165,6 +259,7 @@ void Foam::PatchFunction1Types::MappedFile<Type>::autoMap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear interpolator
|
// Clear interpolator
|
||||||
|
filterFieldPtr_.reset(nullptr);
|
||||||
mapperPtr_.reset(nullptr);
|
mapperPtr_.reset(nullptr);
|
||||||
sampleIndex_ = labelPair(-1, -1);
|
sampleIndex_ = labelPair(-1, -1);
|
||||||
}
|
}
|
||||||
@ -195,6 +290,7 @@ void Foam::PatchFunction1Types::MappedFile<Type>::rmap
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear interpolator
|
// Clear interpolator
|
||||||
|
filterFieldPtr_.reset(nullptr);
|
||||||
mapperPtr_.reset(nullptr);
|
mapperPtr_.reset(nullptr);
|
||||||
sampleIndex_ = labelPair(-1, -1);
|
sampleIndex_ = labelPair(-1, -1);
|
||||||
}
|
}
|
||||||
@ -211,13 +307,41 @@ void Foam::PatchFunction1Types::MappedFile<Type>::updateSampledValues
|
|||||||
tmp<Field<Type>> tvalues;
|
tmp<Field<Type>> tvalues;
|
||||||
|
|
||||||
// Update sampled data fields
|
// Update sampled data fields
|
||||||
|
if (readerPtr_)
|
||||||
|
{
|
||||||
|
wordList fieldNames = readerPtr_->fieldNames(sampleIndex);
|
||||||
|
|
||||||
|
label fieldIndex = fieldNames.find(fieldTableName_);
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "checkTable : Update index=" << sampleIndex
|
||||||
|
<< " field=" << fieldNames[fieldIndex] << endl;
|
||||||
|
|
||||||
|
tvalues = readerPtr_->field
|
||||||
|
(
|
||||||
|
sampleIndex,
|
||||||
|
fieldIndex,
|
||||||
|
pTraits<Type>::zero
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tvalues().size() != mapperPtr_().sourceSize())
|
||||||
|
{
|
||||||
|
FatalErrorInFunction
|
||||||
|
<< "Number of values (" << tvalues().size()
|
||||||
|
<< ") differs from the number of points ("
|
||||||
|
<< mapperPtr_().sourceSize() << ")"
|
||||||
|
<< exit(FatalError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const polyMesh& mesh = this->patch_.boundaryMesh().mesh();
|
const polyMesh& mesh = this->patch_.boundaryMesh().mesh();
|
||||||
const Time& time = mesh.time();
|
const Time& time = mesh.time();
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
Pout<< "checkTable : Reading values from "
|
Pout<< "checkTable : Update index=" << sampleIndex
|
||||||
|
<< " Reading values from "
|
||||||
<<
|
<<
|
||||||
(
|
(
|
||||||
"boundaryData"
|
"boundaryData"
|
||||||
@ -269,6 +393,13 @@ void Foam::PatchFunction1Types::MappedFile<Type>::updateSampledValues
|
|||||||
tvalues = tmp<Field<Type>>::New(std::move(vals.field()));
|
tvalues = tmp<Field<Type>>::New(std::move(vals.field()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filterFieldPtr_)
|
||||||
|
{
|
||||||
|
DebugInfo
|
||||||
|
<< "apply " << filterSweeps_ << " filter sweeps" << endl;
|
||||||
|
|
||||||
|
tvalues = filterFieldPtr_().evaluate(tvalues, filterSweeps_);
|
||||||
|
}
|
||||||
|
|
||||||
// From input values to interpolated (sampled) positions
|
// From input values to interpolated (sampled) positions
|
||||||
field = mapperPtr_().interpolate(tvalues);
|
field = mapperPtr_().interpolate(tvalues);
|
||||||
@ -285,7 +416,74 @@ void Foam::PatchFunction1Types::MappedFile<Type>::checkTable
|
|||||||
const Time& time = mesh.time();
|
const Time& time = mesh.time();
|
||||||
|
|
||||||
// Initialise
|
// Initialise
|
||||||
if (!mapperPtr_)
|
if (!mapperPtr_ && readerPtr_)
|
||||||
|
{
|
||||||
|
auto& reader = readerPtr_();
|
||||||
|
|
||||||
|
const meshedSurface& geom = reader.geometry(0);
|
||||||
|
|
||||||
|
sampleTimes_ = reader.times();
|
||||||
|
|
||||||
|
// We may have been passed in geometry without any faces
|
||||||
|
// eg, boundaryData
|
||||||
|
|
||||||
|
const pointField& samplePoints =
|
||||||
|
(
|
||||||
|
geom.nFaces() ? geom.faceCentres() : geom.points()
|
||||||
|
);
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "Read " << samplePoints.size() << " sample points from "
|
||||||
|
<< readerFile_ << endl
|
||||||
|
<< "Found times "
|
||||||
|
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_) << nl;
|
||||||
|
|
||||||
|
// tbd: run-time selection
|
||||||
|
const bool nearestOnly =
|
||||||
|
(
|
||||||
|
!mapMethod_.empty() && !mapMethod_.starts_with("planar")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Allocate the interpolator
|
||||||
|
if (this->faceValues())
|
||||||
|
{
|
||||||
|
mapperPtr_.reset
|
||||||
|
(
|
||||||
|
new pointToPointPlanarInterpolation
|
||||||
|
(
|
||||||
|
samplePoints,
|
||||||
|
this->localPosition(this->patch_.faceCentres()),
|
||||||
|
perturb_,
|
||||||
|
nearestOnly
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mapperPtr_.reset
|
||||||
|
(
|
||||||
|
new pointToPointPlanarInterpolation
|
||||||
|
(
|
||||||
|
samplePoints,
|
||||||
|
this->localPosition(this->patch_.localPoints()),
|
||||||
|
perturb_,
|
||||||
|
nearestOnly
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup median filter (if any)
|
||||||
|
if (filterSweeps_ > 0)
|
||||||
|
{
|
||||||
|
filterFieldPtr_.reset(new FilterField(geom, filterRadius_));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterFieldPtr_.reset(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!mapperPtr_)
|
||||||
{
|
{
|
||||||
// Reread values and interpolate
|
// Reread values and interpolate
|
||||||
const fileName samplePointsFile
|
const fileName samplePointsFile
|
||||||
@ -349,7 +547,6 @@ void Foam::PatchFunction1Types::MappedFile<Type>::checkTable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Read the times for which data is available
|
// Read the times for which data is available
|
||||||
const fileName samplePointsDir = samplePointsFile.path();
|
const fileName samplePointsDir = samplePointsFile.path();
|
||||||
sampleTimes_ = Time::findTimes(samplePointsDir);
|
sampleTimes_ = Time::findTimes(samplePointsDir);
|
||||||
@ -358,11 +555,21 @@ void Foam::PatchFunction1Types::MappedFile<Type>::checkTable
|
|||||||
<< "Found times "
|
<< "Found times "
|
||||||
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_)
|
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_)
|
||||||
<< endl;
|
<< endl;
|
||||||
|
|
||||||
|
// Setup median filter (if any)
|
||||||
|
if (filterSweeps_ > 0)
|
||||||
|
{
|
||||||
|
filterFieldPtr_.reset(new FilterField(samplePoints, filterRadius_));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filterFieldPtr_.reset(nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Find range of current time indices in sampleTimes
|
// Find range of current time indices in sampleTimes
|
||||||
Pair<label> timeIndices = instant::findRange
|
labelPair timeIndices = instant::findRange
|
||||||
(
|
(
|
||||||
sampleTimes_,
|
sampleTimes_,
|
||||||
t, //mesh.time().value(),
|
t, //mesh.time().value(),
|
||||||
@ -555,6 +762,12 @@ void Foam::PatchFunction1Types::MappedFile<Type>::writeEntries
|
|||||||
Ostream& os
|
Ostream& os
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
|
if (!readerFormat_.empty() && !readerFile_.empty())
|
||||||
|
{
|
||||||
|
os.writeEntry("readerFormat", readerFormat_);
|
||||||
|
os.writeEntry("readerFile", readerFile_);
|
||||||
|
}
|
||||||
|
|
||||||
os.writeEntryIfDifferent
|
os.writeEntryIfDifferent
|
||||||
(
|
(
|
||||||
"fieldTable",
|
"fieldTable",
|
||||||
@ -579,6 +792,12 @@ void Foam::PatchFunction1Types::MappedFile<Type>::writeEntries
|
|||||||
|
|
||||||
os.writeEntryIfDifferent<scalar>("perturb", 1e-5, perturb_);
|
os.writeEntryIfDifferent<scalar>("perturb", 1e-5, perturb_);
|
||||||
|
|
||||||
|
if (filterSweeps_ >= 1)
|
||||||
|
{
|
||||||
|
os.writeEntry("filterRadius", filterRadius_);
|
||||||
|
os.writeEntry("filterSweeps", filterSweeps_);
|
||||||
|
}
|
||||||
|
|
||||||
if (offset_)
|
if (offset_)
|
||||||
{
|
{
|
||||||
offset_->writeData(os);
|
offset_->writeData(os);
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
\\ / A nd | www.openfoam.com
|
\\ / A nd | www.openfoam.com
|
||||||
\\/ M anipulation |
|
\\/ M anipulation |
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
Copyright (C) 2018-2021 OpenCFD Ltd.
|
Copyright (C) 2018-2022 OpenCFD Ltd.
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
License
|
License
|
||||||
This file is part of OpenFOAM.
|
This file is part of OpenFOAM.
|
||||||
@ -49,10 +49,26 @@ Description
|
|||||||
\endtable
|
\endtable
|
||||||
|
|
||||||
Options for the \c mapMethod entry:
|
Options for the \c mapMethod entry:
|
||||||
\verbatim
|
\table
|
||||||
|
Property | Description
|
||||||
nearest | Use nearest points only (avoids triangulation)
|
nearest | Use nearest points only (avoids triangulation)
|
||||||
planar | Interpolation using 2D Delaunay triangulation
|
planar | Interpolation using 2D Delaunay triangulation
|
||||||
\endverbatim
|
\endtable
|
||||||
|
|
||||||
|
Options for reading and filtering:
|
||||||
|
\table
|
||||||
|
Property | Description
|
||||||
|
sampleFormat | The surfaceReader format (eg, ensight)
|
||||||
|
sampleFile | <case>/foo/bar/window.case
|
||||||
|
filterRadius | Search radius [m] for median filter neighbours
|
||||||
|
filterSweeps | Filter sweeps for median filter
|
||||||
|
\endtable
|
||||||
|
|
||||||
|
Note
|
||||||
|
The MappedFile handling currently has two different reading types:
|
||||||
|
a builtin boundaryData reader (default) and a surfaceReader
|
||||||
|
(specified by sampleFormat and sampleFile keywords). The generic
|
||||||
|
surfaceReader handling does \em not support field averaging.
|
||||||
|
|
||||||
SourceFiles
|
SourceFiles
|
||||||
MappedFile.C
|
MappedFile.C
|
||||||
@ -66,6 +82,8 @@ SourceFiles
|
|||||||
#include "Function1.H"
|
#include "Function1.H"
|
||||||
#include "Pair.H"
|
#include "Pair.H"
|
||||||
#include "instantList.H"
|
#include "instantList.H"
|
||||||
|
#include "surfaceReader.H"
|
||||||
|
#include "MappedFileFilterField.H"
|
||||||
#include "pointToPointPlanarInterpolation.H"
|
#include "pointToPointPlanarInterpolation.H"
|
||||||
|
|
||||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
@ -104,6 +122,24 @@ class MappedFile
|
|||||||
//- Interpolation scheme to use (default is empty == "planar")
|
//- Interpolation scheme to use (default is empty == "planar")
|
||||||
word mapMethod_;
|
word mapMethod_;
|
||||||
|
|
||||||
|
//- Radius for filter
|
||||||
|
scalar filterRadius_;
|
||||||
|
|
||||||
|
//- Number of median filter sweeps
|
||||||
|
label filterSweeps_;
|
||||||
|
|
||||||
|
//- Median filtering (for input values)
|
||||||
|
mutable autoPtr<FilterField> filterFieldPtr_;
|
||||||
|
|
||||||
|
//- Format name for surfaceReader
|
||||||
|
word readerFormat_;
|
||||||
|
|
||||||
|
//- File name associated with surfaceReader
|
||||||
|
fileName readerFile_;
|
||||||
|
|
||||||
|
//- Meshed surface with fields (for input values)
|
||||||
|
mutable autoPtr<surfaceReader> readerPtr_;
|
||||||
|
|
||||||
//- 2D interpolation (for 'planar' mapMethod)
|
//- 2D interpolation (for 'planar' mapMethod)
|
||||||
mutable autoPtr<pointToPointPlanarInterpolation> mapperPtr_;
|
mutable autoPtr<pointToPointPlanarInterpolation> mapperPtr_;
|
||||||
|
|
||||||
|
|||||||
381
src/meshTools/PatchFunction1/MappedFile/MappedFileFilterField.C
Normal file
381
src/meshTools/PatchFunction1/MappedFile/MappedFileFilterField.C
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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 "MappedFileFilterField.H"
|
||||||
|
#include "oneField.H"
|
||||||
|
#include "MeshedSurfaces.H"
|
||||||
|
#include "MinMax.H"
|
||||||
|
#include "indexedOctree.H"
|
||||||
|
#include "treeDataPoint.H"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
namespace PatchFunction1Types
|
||||||
|
{
|
||||||
|
defineTypeNameAndDebug(FilterField, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Foam::Enum
|
||||||
|
<
|
||||||
|
Foam::PatchFunction1Types::FilterField::RBF_type
|
||||||
|
>
|
||||||
|
Foam::PatchFunction1Types::FilterField::RBF_typeNames_
|
||||||
|
{
|
||||||
|
{ RBF_type::RBF_linear, "linear" },
|
||||||
|
{ RBF_type::RBF_quadratic, "quadratic" },
|
||||||
|
{ RBF_type::RBF_linear, "default" },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
|
||||||
|
//- Linear basis function: 1 - (r/r0)
|
||||||
|
static inline scalar linearWeight
|
||||||
|
(
|
||||||
|
const point& p,
|
||||||
|
const point& p0,
|
||||||
|
const scalar radiusSqr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return (1 - ::sqrt(p.distSqr(p0) / radiusSqr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//- Quadratic basis function: 1 - (r/r0)^2
|
||||||
|
static inline scalar quadraticWeight
|
||||||
|
(
|
||||||
|
const point& p,
|
||||||
|
const point& p0,
|
||||||
|
const scalar radiusSqr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return (1 - p.distSqr(p0) / radiusSqr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//- Construct search tree for points
|
||||||
|
static autoPtr<indexedOctree<treeDataPoint>>
|
||||||
|
createTree(const pointField& points)
|
||||||
|
{
|
||||||
|
// Avoid bounding box error in case of 2D cases
|
||||||
|
treeBoundBox bb(points);
|
||||||
|
bb.grow(1e-4);
|
||||||
|
|
||||||
|
const int debug = PatchFunction1Types::FilterField::debug;
|
||||||
|
|
||||||
|
const int oldDebug = indexedOctreeBase::debug;
|
||||||
|
|
||||||
|
if (debug & 2)
|
||||||
|
{
|
||||||
|
indexedOctreeBase::debug = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto treePtr = autoPtr<indexedOctree<treeDataPoint>>::New
|
||||||
|
(
|
||||||
|
treeDataPoint(points),
|
||||||
|
bb, // overall search domain
|
||||||
|
points.size(), // maxLevel
|
||||||
|
16, // leafsize
|
||||||
|
1.0 // duplicity
|
||||||
|
);
|
||||||
|
|
||||||
|
indexedOctreeBase::debug = oldDebug;
|
||||||
|
|
||||||
|
if (debug & 2)
|
||||||
|
{
|
||||||
|
const auto& tree = treePtr();
|
||||||
|
|
||||||
|
OFstream os("indexedOctree.obj");
|
||||||
|
tree.writeOBJ(os);
|
||||||
|
Info<< "=================" << endl;
|
||||||
|
Info<< "have " << tree.nodes().size() << " nodes" << nl;
|
||||||
|
Info<< "=================" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return treePtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End namespace Foam
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
template
|
||||||
|
<
|
||||||
|
class TreeType,
|
||||||
|
class RadiusSqrOp,
|
||||||
|
class BasisFunctionOp,
|
||||||
|
class PointWeightFieldType
|
||||||
|
>
|
||||||
|
void Foam::PatchFunction1Types::FilterField::buildWeightsImpl
|
||||||
|
(
|
||||||
|
const TreeType& tree,
|
||||||
|
const RadiusSqrOp& radiusSqrOp,
|
||||||
|
const BasisFunctionOp& basisFuncOp,
|
||||||
|
const PointWeightFieldType& pointWeightFld
|
||||||
|
)
|
||||||
|
{
|
||||||
|
tmp<pointField> tpoints = tree.shapes().centres();
|
||||||
|
|
||||||
|
const auto& points = tpoints();
|
||||||
|
|
||||||
|
const label nPoints = points.size();
|
||||||
|
|
||||||
|
weights_.clear();
|
||||||
|
addressing_.clear();
|
||||||
|
|
||||||
|
weights_.resize(nPoints);
|
||||||
|
addressing_.resize(nPoints);
|
||||||
|
|
||||||
|
labelHashSet neighbours(2*128);
|
||||||
|
|
||||||
|
// Catch degenerate case where weight/addressing not actually needed
|
||||||
|
bool usesNeighbours = false;
|
||||||
|
|
||||||
|
for (label pointi = 0; pointi < nPoints; ++pointi)
|
||||||
|
{
|
||||||
|
const point& p0 = points[pointi];
|
||||||
|
auto& currAddr = addressing_[pointi];
|
||||||
|
auto& currWeight = weights_[pointi];
|
||||||
|
|
||||||
|
// Search with local sphere
|
||||||
|
const scalar radiusSqr = radiusSqrOp(pointi);
|
||||||
|
|
||||||
|
tree.findSphere(p0, radiusSqr, neighbours);
|
||||||
|
|
||||||
|
// Paranoid, enforce identity weighting as a minimum
|
||||||
|
if (neighbours.size() < 2)
|
||||||
|
{
|
||||||
|
currAddr.resize(1, pointi);
|
||||||
|
currWeight.resize(1, scalar(1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usesNeighbours = true;
|
||||||
|
|
||||||
|
currAddr = neighbours.sortedToc();
|
||||||
|
currWeight.resize_nocopy(currAddr.size());
|
||||||
|
|
||||||
|
scalar sumWeight = 0;
|
||||||
|
|
||||||
|
forAll(currAddr, i)
|
||||||
|
{
|
||||||
|
const point& p = points[currAddr[i]];
|
||||||
|
|
||||||
|
currWeight[i] = basisFuncOp(p, p0, radiusSqr);
|
||||||
|
|
||||||
|
// Eg, face area weighting.
|
||||||
|
// - cast required for oneField()
|
||||||
|
currWeight[i] *= static_cast<scalar>(pointWeightFld[i]);
|
||||||
|
sumWeight += currWeight[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& w : currWeight)
|
||||||
|
{
|
||||||
|
w /= sumWeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usesNeighbours)
|
||||||
|
{
|
||||||
|
// No addressing/weighting required
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug && !addressing_.empty())
|
||||||
|
{
|
||||||
|
labelMinMax limits(addressing_[0].size());
|
||||||
|
|
||||||
|
label total = 0;
|
||||||
|
|
||||||
|
for (const labelList& addr : addressing_)
|
||||||
|
{
|
||||||
|
const label n = addr.size();
|
||||||
|
|
||||||
|
limits.add(n);
|
||||||
|
total += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< "Weight neighbours: min=" << limits.min()
|
||||||
|
<< " avg=" << (total / addressing_.size())
|
||||||
|
<< " max=" << limits.max() << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
Foam::PatchFunction1Types::FilterField::FilterField
|
||||||
|
(
|
||||||
|
const pointField& points,
|
||||||
|
const scalar radius,
|
||||||
|
const RBF_type interp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
reset(points, radius, interp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::PatchFunction1Types::FilterField::FilterField
|
||||||
|
(
|
||||||
|
const meshedSurface& geom,
|
||||||
|
const scalar radius,
|
||||||
|
const bool relative,
|
||||||
|
const RBF_type interp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
reset(geom, radius, relative, interp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
void Foam::PatchFunction1Types::FilterField::reset()
|
||||||
|
{
|
||||||
|
addressing_.clear();
|
||||||
|
weights_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PatchFunction1Types::FilterField::reset
|
||||||
|
(
|
||||||
|
const pointField& points,
|
||||||
|
const scalar radius,
|
||||||
|
const RBF_type interp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
scalarField dummyWeights;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
Info<< "Apply " << RBF_typeNames_[interp] << " filter,"
|
||||||
|
<< " radius=" << radius << nl
|
||||||
|
<< "Create tree..." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoPtr<indexedOctree<treeDataPoint>> treePtr
|
||||||
|
= createTree(points);
|
||||||
|
|
||||||
|
const scalar radiusSqr = sqr(radius);
|
||||||
|
|
||||||
|
auto weightFunc = linearWeight;
|
||||||
|
|
||||||
|
if (interp == RBF_type::RBF_quadratic)
|
||||||
|
{
|
||||||
|
weightFunc = quadraticWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Use (constant) absolute size
|
||||||
|
buildWeightsImpl
|
||||||
|
(
|
||||||
|
treePtr(),
|
||||||
|
[=](label index) -> scalar { return radiusSqr; },
|
||||||
|
weightFunc,
|
||||||
|
oneField()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PatchFunction1Types::FilterField::reset
|
||||||
|
(
|
||||||
|
const meshedSurface& geom,
|
||||||
|
const scalar radius,
|
||||||
|
const bool relative,
|
||||||
|
const RBF_type interp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!geom.nFaces())
|
||||||
|
{
|
||||||
|
// Received geometry without any faces eg, boundaryData
|
||||||
|
reset(geom.points(), radius, interp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
Info<< "Apply " << RBF_typeNames_[interp] << " filter,";
|
||||||
|
|
||||||
|
if (relative)
|
||||||
|
{
|
||||||
|
Info<< " relative";
|
||||||
|
}
|
||||||
|
|
||||||
|
Info<< " radius=" << radius << endl;
|
||||||
|
Info<< "Create tree..." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
autoPtr<indexedOctree<treeDataPoint>> treePtr
|
||||||
|
= createTree(geom.faceCentres());
|
||||||
|
|
||||||
|
const scalar radiusSqr = sqr(radius);
|
||||||
|
|
||||||
|
auto weightFunc = linearWeight;
|
||||||
|
|
||||||
|
if (interp == RBF_type::RBF_quadratic)
|
||||||
|
{
|
||||||
|
weightFunc = quadraticWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (relative)
|
||||||
|
{
|
||||||
|
// Use relative size
|
||||||
|
buildWeightsImpl
|
||||||
|
(
|
||||||
|
treePtr(),
|
||||||
|
[&](label index) -> scalar
|
||||||
|
{
|
||||||
|
return (radiusSqr * geom.sphere(index));
|
||||||
|
},
|
||||||
|
weightFunc,
|
||||||
|
geom.magSf()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use (constant) absolute size
|
||||||
|
buildWeightsImpl
|
||||||
|
(
|
||||||
|
treePtr(),
|
||||||
|
[=](label index) -> scalar { return radiusSqr; },
|
||||||
|
weightFunc,
|
||||||
|
geom.magSf()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
213
src/meshTools/PatchFunction1/MappedFile/MappedFileFilterField.H
Normal file
213
src/meshTools/PatchFunction1/MappedFile/MappedFileFilterField.H
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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/>.
|
||||||
|
|
||||||
|
Class
|
||||||
|
Foam::PatchFunction1Types::FilterField
|
||||||
|
|
||||||
|
Description
|
||||||
|
The FilterField helper class provides a multi-sweep median filter
|
||||||
|
for a Field of data associated with a geometric point cloud.
|
||||||
|
|
||||||
|
The points can be freestanding or the faceCentres (or points)
|
||||||
|
of a meshedSurface, for example.
|
||||||
|
|
||||||
|
Using an initial specified search radius, the nearest point
|
||||||
|
neighbours are gathered and addressing/weights are built for them.
|
||||||
|
This currently uses an area-weighted, linear RBF interpolator
|
||||||
|
with provision for quadratic RBF interpolator etc.
|
||||||
|
|
||||||
|
After the weights and addressing are established,
|
||||||
|
the evaluate() method can be called to apply a median filter
|
||||||
|
to data fields, with a specified number of sweeps.
|
||||||
|
|
||||||
|
Note
|
||||||
|
When handling large search radii and/or an extensive number of
|
||||||
|
filter sweeps, compiling with openmp can yield some speedup.
|
||||||
|
|
||||||
|
SourceFiles
|
||||||
|
MappedFileFilterField.C
|
||||||
|
MappedFileFilterFieldTemplates.C
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef Foam_PatchFunction1Types_MappedFileFilterField_H
|
||||||
|
#define Foam_PatchFunction1Types_MappedFileFilterField_H
|
||||||
|
|
||||||
|
#include "primitiveFields.H"
|
||||||
|
#include "pointField.H"
|
||||||
|
#include "Enum.H"
|
||||||
|
#include "className.H"
|
||||||
|
#include "MeshedSurfacesFwd.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
namespace PatchFunction1Types
|
||||||
|
{
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
Class FilterField Declaration
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class FilterField
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Data Types
|
||||||
|
|
||||||
|
//- Basis type
|
||||||
|
enum RBF_type
|
||||||
|
{
|
||||||
|
RBF_linear,
|
||||||
|
RBF_quadratic
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Private Data
|
||||||
|
|
||||||
|
//- Names for RBF basis types
|
||||||
|
static const Enum<RBF_type> RBF_typeNames_;
|
||||||
|
|
||||||
|
//- Addressing
|
||||||
|
List<labelList> addressing_;
|
||||||
|
|
||||||
|
//- Weights
|
||||||
|
List<scalarField> weights_;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Functions
|
||||||
|
|
||||||
|
//- Construct neighbour point weights and addressing based
|
||||||
|
//- on specified search radius
|
||||||
|
//
|
||||||
|
// Search with local sphere
|
||||||
|
//
|
||||||
|
// RadiusSqrOp (index) -> radiusSqr
|
||||||
|
// BasisFunctionOp(point, point, radius^2) -> RBF
|
||||||
|
// PointWeightFieldType [index] -> weighting
|
||||||
|
template
|
||||||
|
<
|
||||||
|
class TreeType,
|
||||||
|
class RadiusSqrOp,
|
||||||
|
class BasisFunctionOp,
|
||||||
|
class PointWeightFieldType
|
||||||
|
>
|
||||||
|
void buildWeightsImpl
|
||||||
|
(
|
||||||
|
const TreeType& tree,
|
||||||
|
const RadiusSqrOp& radiusSqrOp,
|
||||||
|
const BasisFunctionOp& basisFuncOp,
|
||||||
|
const PointWeightFieldType& pointWeightFld
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//- Runtime type information
|
||||||
|
ClassName("filterField");
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
//- Default construct
|
||||||
|
FilterField() noexcept = default;
|
||||||
|
|
||||||
|
//- Construct with weights for a field of points
|
||||||
|
//- (constant search radius)
|
||||||
|
FilterField
|
||||||
|
(
|
||||||
|
const pointField& points,
|
||||||
|
const scalar radius,
|
||||||
|
const RBF_type interp = RBF_type::RBF_linear
|
||||||
|
);
|
||||||
|
|
||||||
|
//- Construct with weights for faceCentres of a meshedSurface
|
||||||
|
//- using a constant search radius search
|
||||||
|
//- (or relative radius multiplier for the face bounding spheres).
|
||||||
|
FilterField
|
||||||
|
(
|
||||||
|
const meshedSurface& geom,
|
||||||
|
const scalar radius,
|
||||||
|
const bool relative = false,
|
||||||
|
const RBF_type interp = RBF_type::RBF_linear
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Member Functions
|
||||||
|
|
||||||
|
//- Reset to unweighted (pass-through)
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
//- Create weights for field of points (constant search radius)
|
||||||
|
void reset
|
||||||
|
(
|
||||||
|
const pointField& points,
|
||||||
|
const scalar radius,
|
||||||
|
const RBF_type interp = RBF_type::RBF_linear
|
||||||
|
);
|
||||||
|
|
||||||
|
//- Create weights for meshedSurface using a constant search radius
|
||||||
|
//- or optionally with a search radius multiplier for the
|
||||||
|
//- face bounding spheres.
|
||||||
|
void reset
|
||||||
|
(
|
||||||
|
const meshedSurface& geom,
|
||||||
|
const scalar radius,
|
||||||
|
const bool relative = false,
|
||||||
|
const RBF_type interp = RBF_type::RBF_linear
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Evaluation
|
||||||
|
|
||||||
|
//- Return the median smoothed field
|
||||||
|
template<class Type>
|
||||||
|
tmp<Field<Type>> evaluate
|
||||||
|
(
|
||||||
|
const tmp<Field<Type>>& tinput,
|
||||||
|
const label nSweeps
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
} // End namespace PatchFunction1Types
|
||||||
|
} // End namespace Foam
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
#ifdef NoRepository
|
||||||
|
#include "MappedFileFilterFieldTemplates.C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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 "indexedOctree.H"
|
||||||
|
#include "treeDataPoint.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
template<class Type>
|
||||||
|
Foam::tmp<Foam::Field<Type>>
|
||||||
|
Foam::PatchFunction1Types::FilterField::evaluate
|
||||||
|
(
|
||||||
|
const tmp<Field<Type>>& tinput,
|
||||||
|
const label nSweeps
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
if (nSweeps < 1 || !tinput.good())
|
||||||
|
{
|
||||||
|
// Nothing to do
|
||||||
|
return tinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
label nPoints = tinput().size();
|
||||||
|
|
||||||
|
const label nAddr = addressing_.size();
|
||||||
|
|
||||||
|
if (!nPoints || !nAddr)
|
||||||
|
{
|
||||||
|
// No input or using an identity mapping
|
||||||
|
return tinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output
|
||||||
|
auto toutput = tmp<Field<Type>>::New(nPoints);
|
||||||
|
|
||||||
|
if (nAddr < nPoints)
|
||||||
|
{
|
||||||
|
WarningInFunction
|
||||||
|
<< "Addressing/weights shorter than input field"
|
||||||
|
<< endl;
|
||||||
|
|
||||||
|
// Restrict addressing space within loop
|
||||||
|
nPoints = nAddr;
|
||||||
|
|
||||||
|
// Fill trailing portion with the input values
|
||||||
|
toutput.ref().slice(nAddr) = tinput().slice(nAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intermediate buffer
|
||||||
|
tmp<Field<Type>> tbuffer;
|
||||||
|
|
||||||
|
if (nSweeps == 1)
|
||||||
|
{
|
||||||
|
// Simple reference ie enough
|
||||||
|
tbuffer.cref(tinput);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need buffer for swap - copy or steal contents
|
||||||
|
tbuffer.reset(tinput.ptr());
|
||||||
|
}
|
||||||
|
tinput.clear();
|
||||||
|
|
||||||
|
// If there are any 'senseless' sweeps
|
||||||
|
// (ie, with identity mapping of values), they will have
|
||||||
|
// been detected during addressing construction, can ignore here
|
||||||
|
|
||||||
|
for (label sweep = 0; sweep < nSweeps; ++sweep)
|
||||||
|
{
|
||||||
|
if (sweep > 0)
|
||||||
|
{
|
||||||
|
// Reuse previous output for subsequent sweeps
|
||||||
|
tbuffer.swap(toutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& input = tbuffer();
|
||||||
|
auto& output = toutput.ref();
|
||||||
|
|
||||||
|
#pragma omp parallel for if (nPoints > 1000)
|
||||||
|
for (label pointi = 0; pointi < nPoints; ++pointi)
|
||||||
|
{
|
||||||
|
const auto& addr = addressing_[pointi];
|
||||||
|
const auto& weight = weights_[pointi];
|
||||||
|
|
||||||
|
auto& interp = output[pointi];
|
||||||
|
|
||||||
|
if (addr.empty())
|
||||||
|
{
|
||||||
|
// Could happen if the search radius was really small
|
||||||
|
interp = input[pointi];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
interp = Zero;
|
||||||
|
|
||||||
|
forAll(addr, i)
|
||||||
|
{
|
||||||
|
interp += (weight[i] * input[addr[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -60,6 +60,7 @@ triSurface/patches/surfacePatch.C
|
|||||||
readers = readers
|
readers = readers
|
||||||
|
|
||||||
$(readers)/common/surfaceReader.C
|
$(readers)/common/surfaceReader.C
|
||||||
|
$(readers)/boundary/boundaryDataSurfaceReader.C
|
||||||
$(readers)/ensight/ensightSurfaceReader.C
|
$(readers)/ensight/ensightSurfaceReader.C
|
||||||
|
|
||||||
writers = writers
|
writers = writers
|
||||||
|
|||||||
281
src/surfMesh/readers/boundary/boundaryDataSurfaceReader.C
Normal file
281
src/surfMesh/readers/boundary/boundaryDataSurfaceReader.C
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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 "boundaryDataSurfaceReader.H"
|
||||||
|
#include "rawIOField.H"
|
||||||
|
#include "argList.H"
|
||||||
|
#include "Time.H"
|
||||||
|
#include "addToRunTimeSelectionTable.H"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
defineTypeNameAndDebug(boundaryDataSurfaceReader, 0);
|
||||||
|
addToRunTimeSelectionTable
|
||||||
|
(
|
||||||
|
surfaceReader,
|
||||||
|
boundaryDataSurfaceReader,
|
||||||
|
fileName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
Foam::pointField Foam::boundaryDataSurfaceReader::readPoints
|
||||||
|
(
|
||||||
|
const Time& runTime,
|
||||||
|
const fileName& baseDir,
|
||||||
|
const word& pointsName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fileName pointsFile
|
||||||
|
(
|
||||||
|
baseDir / (pointsName.empty() ? "points" : pointsName)
|
||||||
|
);
|
||||||
|
pointsFile.toAbsolute();
|
||||||
|
|
||||||
|
IOobject io
|
||||||
|
(
|
||||||
|
pointsFile, // absolute path
|
||||||
|
runTime,
|
||||||
|
IOobject::MUST_READ,
|
||||||
|
IOobject::NO_WRITE,
|
||||||
|
IOobject::NO_REGISTER,
|
||||||
|
true // global object (currently not used)
|
||||||
|
);
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "File: " << io.objectPath() << endl;
|
||||||
|
|
||||||
|
// Read data (no average value!)
|
||||||
|
rawIOField<point> rawData(io);
|
||||||
|
|
||||||
|
pointField points(std::move(rawData.field()));
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "File: " << io.objectPath()
|
||||||
|
<< " " << points.size() << " points" << endl;
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::pointField Foam::boundaryDataSurfaceReader::readPoints
|
||||||
|
(
|
||||||
|
const fileName& dirName,
|
||||||
|
const word& pointsName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
refPtr<Time> timePtr(Time::New(argList::envGlobalPath()));
|
||||||
|
|
||||||
|
return readPoints(*timePtr, dirName, pointsName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
void Foam::boundaryDataSurfaceReader::readCase()
|
||||||
|
{
|
||||||
|
DebugInFunction << endl;
|
||||||
|
|
||||||
|
timeValues_ = TimePaths::findTimes(baseDir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::boundaryDataSurfaceReader::readGeometry
|
||||||
|
(
|
||||||
|
meshedSurface& surf,
|
||||||
|
const label timeIndex
|
||||||
|
)
|
||||||
|
{
|
||||||
|
surf.clear();
|
||||||
|
|
||||||
|
pointField points(this->readPoints(baseDir_, pointsName_));
|
||||||
|
|
||||||
|
surf = meshedSurface(std::move(points), faceList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Type>
|
||||||
|
Foam::tmp<Foam::Field<Type>>
|
||||||
|
Foam::boundaryDataSurfaceReader::readFieldTemplate
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
Type dummyAvg;
|
||||||
|
|
||||||
|
return readField<Type>
|
||||||
|
(
|
||||||
|
baseDir_,
|
||||||
|
timeValues_[timeIndex],
|
||||||
|
fieldNames_[fieldIndex],
|
||||||
|
dummyAvg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
Foam::boundaryDataSurfaceReader::boundaryDataSurfaceReader
|
||||||
|
(
|
||||||
|
const fileName& fName,
|
||||||
|
const word& pointsName
|
||||||
|
)
|
||||||
|
:
|
||||||
|
surfaceReader(fName),
|
||||||
|
baseDir_(fName.path()),
|
||||||
|
pointsName_(pointsName),
|
||||||
|
timeValues_(),
|
||||||
|
fieldNames_(),
|
||||||
|
surfPtr_(nullptr)
|
||||||
|
{
|
||||||
|
baseDir_.toAbsolute();
|
||||||
|
debug = 1;
|
||||||
|
DebugInFunction << endl;
|
||||||
|
Info<< "create with " << baseDir_ << endl;
|
||||||
|
|
||||||
|
readCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Public Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
const Foam::meshedSurface& Foam::boundaryDataSurfaceReader::geometry
|
||||||
|
(
|
||||||
|
const label timeIndex
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DebugInFunction << endl;
|
||||||
|
|
||||||
|
if (!surfPtr_)
|
||||||
|
{
|
||||||
|
surfPtr_.reset(new meshedSurface);
|
||||||
|
readGeometry(*surfPtr_, timeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *surfPtr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::instantList Foam::boundaryDataSurfaceReader::times() const
|
||||||
|
{
|
||||||
|
return timeValues_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::wordList Foam::boundaryDataSurfaceReader::fieldNames
|
||||||
|
(
|
||||||
|
const label timeIndex
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
if (timeValues_.empty() || timeIndex >= timeValues_.size())
|
||||||
|
{
|
||||||
|
fieldNames_.clear();
|
||||||
|
return wordList();
|
||||||
|
}
|
||||||
|
|
||||||
|
fileNameList items =
|
||||||
|
fileHandler().readDir(baseDir_/timeValues_[timeIndex].name());
|
||||||
|
|
||||||
|
fieldNames_.resize_nocopy(items.size());
|
||||||
|
|
||||||
|
std::transform
|
||||||
|
(
|
||||||
|
items.begin(),
|
||||||
|
items.end(),
|
||||||
|
fieldNames_.begin(),
|
||||||
|
[](const fileName& f) { return word(f); }
|
||||||
|
);
|
||||||
|
|
||||||
|
Foam::sort(fieldNames_);
|
||||||
|
|
||||||
|
return fieldNames_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::tmp<Foam::Field<Foam::scalar>> Foam::boundaryDataSurfaceReader::field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const scalar& refValue
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return readFieldTemplate<scalar>(timeIndex, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::tmp<Foam::Field<Foam::vector>> Foam::boundaryDataSurfaceReader::field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const vector& refValue
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return readFieldTemplate<vector>(timeIndex, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::tmp<Foam::Field<Foam::sphericalTensor>>
|
||||||
|
Foam::boundaryDataSurfaceReader::field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const sphericalTensor& refValue
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return readFieldTemplate<sphericalTensor>(timeIndex, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::tmp<Foam::Field<Foam::symmTensor>> Foam::boundaryDataSurfaceReader::field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const symmTensor& refValue
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return readFieldTemplate<symmTensor>(timeIndex, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::tmp<Foam::Field<Foam::tensor>> Foam::boundaryDataSurfaceReader::field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const tensor& refValue
|
||||||
|
) const
|
||||||
|
{
|
||||||
|
return readFieldTemplate<tensor>(timeIndex, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
227
src/surfMesh/readers/boundary/boundaryDataSurfaceReader.H
Normal file
227
src/surfMesh/readers/boundary/boundaryDataSurfaceReader.H
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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/>.
|
||||||
|
|
||||||
|
Class
|
||||||
|
Foam::boundaryDataSurfaceReader
|
||||||
|
|
||||||
|
Description
|
||||||
|
A boundaryData format surface reader.
|
||||||
|
However, the "surface" represented by boundaryData is actually only
|
||||||
|
point data!
|
||||||
|
|
||||||
|
// Points
|
||||||
|
<case>/constant/region0/"boundaryData"/patchName/points
|
||||||
|
|
||||||
|
// Values
|
||||||
|
<case>/constant/region0/"boundaryData"/patchName/TIME/field
|
||||||
|
|
||||||
|
SourceFiles
|
||||||
|
boundaryDataSurfaceReader.C
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef Foam_boundaryDataSurfaceReader_H
|
||||||
|
#define Foam_boundaryDataSurfaceReader_H
|
||||||
|
|
||||||
|
#include "surfaceReader.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
class Time;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
Class boundaryDataSurfaceReader Declaration
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class boundaryDataSurfaceReader
|
||||||
|
:
|
||||||
|
public surfaceReader
|
||||||
|
{
|
||||||
|
// Private Data
|
||||||
|
|
||||||
|
//- Base directory
|
||||||
|
fileName baseDir_;
|
||||||
|
|
||||||
|
//- Name of the 'points' file
|
||||||
|
word pointsName_;
|
||||||
|
|
||||||
|
//- Times
|
||||||
|
instantList timeValues_;
|
||||||
|
|
||||||
|
//- Field names
|
||||||
|
mutable List<word> fieldNames_;
|
||||||
|
|
||||||
|
//- Pointer to the surface
|
||||||
|
autoPtr<meshedSurface> surfPtr_;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Functions
|
||||||
|
|
||||||
|
//- Populate time instances
|
||||||
|
void readCase();
|
||||||
|
|
||||||
|
//- Read geometry
|
||||||
|
void readGeometry(meshedSurface& surf, const label timeIndex);
|
||||||
|
|
||||||
|
//- Read and return given field
|
||||||
|
template<class Type>
|
||||||
|
tmp<Field<Type>> readFieldTemplate
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex
|
||||||
|
) const;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//- Runtime type information
|
||||||
|
TypeName("boundaryData");
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
//- Construct from fileName
|
||||||
|
explicit boundaryDataSurfaceReader
|
||||||
|
(
|
||||||
|
const fileName& fName,
|
||||||
|
const word& pointsName = "points"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//- Destructor
|
||||||
|
virtual ~boundaryDataSurfaceReader() = default;
|
||||||
|
|
||||||
|
|
||||||
|
// Static Member Functions
|
||||||
|
|
||||||
|
//- Read points file
|
||||||
|
static pointField readPoints
|
||||||
|
(
|
||||||
|
const Time& runTime,
|
||||||
|
const fileName& baseDir,
|
||||||
|
const word& pointsName = "points"
|
||||||
|
);
|
||||||
|
|
||||||
|
//- Read points file
|
||||||
|
static pointField readPoints
|
||||||
|
(
|
||||||
|
const fileName& baseDir,
|
||||||
|
const word& pointsName = "points"
|
||||||
|
);
|
||||||
|
|
||||||
|
//- Read and return given field
|
||||||
|
template<class Type>
|
||||||
|
static tmp<Field<Type>> readField
|
||||||
|
(
|
||||||
|
const Time& runTime,
|
||||||
|
const fileName& baseDir,
|
||||||
|
const instant& timeDir,
|
||||||
|
const word& fieldName,
|
||||||
|
Type& avg
|
||||||
|
);
|
||||||
|
|
||||||
|
//- Read and return given field
|
||||||
|
template<class Type>
|
||||||
|
static tmp<Field<Type>> readField
|
||||||
|
(
|
||||||
|
const fileName& baseDir,
|
||||||
|
const instant& timeDir,
|
||||||
|
const word& fieldName,
|
||||||
|
Type& avg
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Member Functions
|
||||||
|
|
||||||
|
//- Return a reference to the surface geometry
|
||||||
|
virtual const meshedSurface& geometry(const label timeIndex);
|
||||||
|
|
||||||
|
//- Return a list of the available times
|
||||||
|
virtual instantList times() const;
|
||||||
|
|
||||||
|
//- Return a list of the available fields at a given time
|
||||||
|
virtual wordList fieldNames(const label timeIndex) const;
|
||||||
|
|
||||||
|
//- Return a scalar field at a given time
|
||||||
|
virtual tmp<Field<scalar>> field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const scalar& refValue = pTraits<scalar>::zero
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Return a vector field at a given time
|
||||||
|
virtual tmp<Field<vector>> field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const vector& refValue = pTraits<vector>::zero
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Return a sphericalTensor field at a given time
|
||||||
|
virtual tmp<Field<sphericalTensor>> field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const sphericalTensor& refValue = pTraits<sphericalTensor>::zero
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Return a symmTensor field at a given time
|
||||||
|
virtual tmp<Field<symmTensor>> field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const symmTensor& refValue = pTraits<symmTensor>::zero
|
||||||
|
) const;
|
||||||
|
|
||||||
|
//- Return a tensor field at a given time
|
||||||
|
virtual tmp<Field<tensor>> field
|
||||||
|
(
|
||||||
|
const label timeIndex,
|
||||||
|
const label fieldIndex,
|
||||||
|
const tensor& refValue = pTraits<tensor>::zero
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
} // End namespace Foam
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
#ifdef NoRepository
|
||||||
|
#include "boundaryDataSurfaceReaderTemplates.C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||||
|
\\ / O peration |
|
||||||
|
\\ / A nd | www.openfoam.com
|
||||||
|
\\/ M anipulation |
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Copyright (C) 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 "boundaryDataSurfaceReader.H"
|
||||||
|
#include "rawIOField.H"
|
||||||
|
#include "argList.H"
|
||||||
|
#include "Time.H"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
template<class Type>
|
||||||
|
Foam::tmp<Foam::Field<Type>>
|
||||||
|
Foam::boundaryDataSurfaceReader::readField
|
||||||
|
(
|
||||||
|
const Time& runTime,
|
||||||
|
const fileName& baseDir,
|
||||||
|
const instant& timeDir,
|
||||||
|
const word& fieldName,
|
||||||
|
Type& avg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Reread values and interpolate
|
||||||
|
fileName valuesFile(baseDir/timeDir.name()/fieldName);
|
||||||
|
valuesFile.toAbsolute();
|
||||||
|
|
||||||
|
IOobject io
|
||||||
|
(
|
||||||
|
valuesFile, // absolute path
|
||||||
|
runTime,
|
||||||
|
IOobject::MUST_READ,
|
||||||
|
IOobject::NO_WRITE,
|
||||||
|
IOobject::NO_REGISTER,
|
||||||
|
true // global object (currently not used)
|
||||||
|
);
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "File: " << io.objectPath() << endl;
|
||||||
|
|
||||||
|
// Read data (TDB: setAverage)
|
||||||
|
rawIOField<Type> rawData(io, IOobjectOption::READ_IF_PRESENT);
|
||||||
|
|
||||||
|
if (rawData.hasAverage())
|
||||||
|
{
|
||||||
|
avg = rawData.average();
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugInfo
|
||||||
|
<< "File: " << io.objectPath()
|
||||||
|
<< " " << rawData.size() << " values" << endl;
|
||||||
|
|
||||||
|
return tmp<Field<Type>>::New(std::move(rawData.field()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Type>
|
||||||
|
Foam::tmp<Foam::Field<Type>>
|
||||||
|
Foam::boundaryDataSurfaceReader::readField
|
||||||
|
(
|
||||||
|
const fileName& baseDir,
|
||||||
|
const instant& timeDir,
|
||||||
|
const word& fieldName,
|
||||||
|
Type& avg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
refPtr<Time> timePtr(Time::New(argList::envGlobalPath()));
|
||||||
|
|
||||||
|
return readField<Type>(baseDir, timeDir, fieldName, avg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
Reference in New Issue
Block a user