ENH: additional handling for out-of-range sampling (#1891)

- when sampling onto a meshed surface, the sampling surface may be
  outside of the mesh region, or simply too far away to be considered
  reasonable.

  Can now specify a max search distance and default values for samples
  that are too distant.
  If a default value is not specified, uses Type(Zero).

  Eg,

      maxDistance     0.005;
      defaultValue
      {
          "p.*"   1e5;
          T       273.15;
          U       (-100 -100 -100);
      }
This commit is contained in:
Mark Olesen 2020-10-21 22:10:41 +02:00
parent a947e9ddcf
commit f8d08a805b
6 changed files with 250 additions and 116 deletions

View File

@ -170,7 +170,7 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
// Does approximation by looking at the face centres only
const pointField& fc = surface_.faceCentres();
List<nearInfo> nearest(fc.size(), nearInfo(GREAT, labelMax));
List<nearInfo> nearest(fc.size(), nearInfo(Foam::sqr(GREAT), labelMax));
if (sampleSource_ == samplingSource::cells)
{
@ -181,13 +181,14 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(fc, facei)
{
const point& pt = fc[facei];
auto& near = nearest[facei];
pointIndexHit info = cellTree.findNearest(pt, sqr(GREAT));
pointIndexHit info = cellTree.findNearest(pt, Foam::sqr(GREAT));
if (info.hit())
{
nearest[facei].first() = magSqr(info.hitPoint()-pt);
nearest[facei].second() = globalCells.toGlobal(info.index());
near.first() = magSqr(info.hitPoint()-pt);
near.second() = globalCells.toGlobal(info.index());
}
}
}
@ -200,14 +201,15 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(fc, facei)
{
const point& pt = fc[facei];
auto& near = nearest[facei];
if (cellTree.bb().contains(pt))
{
const label index = cellTree.findInside(pt);
if (index != -1)
{
nearest[facei].first() = 0;
nearest[facei].second() = globalCells.toGlobal(index);
near.first() = 0;
near.second() = globalCells.toGlobal(index);
}
}
}
@ -222,13 +224,14 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(fc, facei)
{
const point& pt = fc[facei];
auto& near = nearest[facei];
pointIndexHit info = bndTree.findNearest(pt, sqr(GREAT));
if (info.hit())
{
nearest[facei].first() = magSqr(info.hitPoint()-pt);
nearest[facei].second() =
near.first() = magSqr(info.hitPoint()-pt);
near.second() =
globalCells.toGlobal
(
bndTree.shapes().faceLabels()[info.index()]
@ -250,16 +253,26 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(nearest, facei)
{
const label index = nearest[facei].second();
const auto& near = nearest[facei];
const label index = near.second();
if (index == labelMax)
{
// Not found on any processor. How to map?
// Not found on any processor, or simply too far.
// How to map?
}
else if (globalCells.isLocal(index))
{
cellOrFaceLabels[facei] = globalCells.toLocal(index);
facesToSubset.set(facei);
// Mark as special if too far away
cellOrFaceLabels[facei] =
(
(near.first() < maxDistanceSqr_)
? globalCells.toLocal(index)
: -1
);
}
}
@ -295,7 +308,7 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
{
// With point interpolation
samplePoints_.resize(pointMap.size());
samplePoints_ = points();
sampleElements_.resize(pointMap.size(), -1);
// Store any face per point (without using pointFaces())
@ -314,32 +327,25 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
if (sampleSource_ == samplingSource::cells)
{
// samplePoints_ : per surface point a location inside the cell
// sampleElements_ : per surface point the cell
// sampleElements_ : per surface point, the cell
// samplePoints_ : per surface point, a location inside the cell
forAll(points(), pointi)
forAll(samplePoints_, pointi)
{
const point& pt = points()[pointi];
// Use _copy_ of point
const point pt = samplePoints_[pointi];
const label celli = cellOrFaceLabels[pointToFace[pointi]];
sampleElements_[pointi] = celli;
// Check if point inside cell
if
(
mesh().pointInCell
(
pt,
sampleElements_[pointi],
meshSearcher.decompMode()
)
celli >= 0
&& !mesh().pointInCell(pt, celli, meshSearcher.decompMode())
)
{
samplePoints_[pointi] = pt;
}
else
{
// Point not inside cell
// Find nearest point on faces of cell
scalar minDistSqr = VGREAT;
@ -348,7 +354,12 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
{
const face& f = mesh().faces()[facei];
pointHit info = f.nearestPoint(pt, mesh().points());
pointHit info =
f.nearestPoint
(
pt,
mesh().points()
);
if (info.distance() < minDistSqr)
{
@ -361,50 +372,52 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
}
else if (sampleSource_ == samplingSource::insideCells)
{
// samplePoints_ : per surface point a location inside the cell
// sampleElements_ : per surface point the cell
// samplePoints_ : per surface point a location inside the cell
forAll(points(), pointi)
forAll(samplePoints_, pointi)
{
const point& pt = points()[pointi];
const label celli = cellOrFaceLabels[pointToFace[pointi]];
sampleElements_[pointi] = celli;
samplePoints_[pointi] = pt;
}
}
else // samplingSource::boundaryFaces
{
// samplePoints_ : per surface point a location on the boundary
// sampleElements_ : per surface point the boundary face containing
// sampleElements_ : per surface point, the boundary face containing
// the location
// samplePoints_ : per surface point, a location on the boundary
forAll(points(), pointi)
forAll(samplePoints_, pointi)
{
const point& pt = points()[pointi];
const point& pt = samplePoints_[pointi];
const label facei = cellOrFaceLabels[pointToFace[pointi]];
sampleElements_[pointi] = facei;
samplePoints_[pointi] = mesh().faces()[facei].nearestPoint
(
pt,
mesh().points()
).rawPoint();
if (facei >= 0)
{
samplePoints_[pointi] =
mesh().faces()[facei].nearestPoint
(
pt,
mesh().points()
).rawPoint();
}
}
}
}
else
{
// if sampleSource_ == cells:
// sampleElements_ : per surface triangle the cell
// sampleElements_ : per surface face, the cell
// samplePoints_ : n/a
// if sampleSource_ == insideCells:
// sampleElements_ : -1 or per surface triangle the cell
// sampleElements_ : -1 or per surface face, the cell
// samplePoints_ : n/a
// else:
// sampleElements_ : per surface triangle the boundary face
// if sampleSource_ == boundaryFaces:
// sampleElements_ : per surface face, the boundary face
// samplePoints_ : n/a
sampleElements_.transfer(cellOrFaceLabels);
@ -432,14 +445,14 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(samplePoints_, pointi)
{
meshTools::writeOBJ(str, points()[pointi]);
vertI++;
++vertI;
meshTools::writeOBJ(str, samplePoints_[pointi]);
vertI++;
++vertI;
label elemi = sampleElements_[pointi];
meshTools::writeOBJ(str, centres[elemi]);
vertI++;
++vertI;
str << "l " << vertI-2 << ' ' << vertI-1 << ' ' << vertI << nl;
}
}
@ -449,11 +462,11 @@ bool Foam::sampledMeshedSurface::update(const meshSearch& meshSearcher)
forAll(sampleElements_, triI)
{
meshTools::writeOBJ(str, faceCentres()[triI]);
vertI++;
++vertI;
label elemi = sampleElements_[triI];
meshTools::writeOBJ(str, centres[elemi]);
vertI++;
++vertI;
str << "l " << vertI-1 << ' ' << vertI << nl;
}
}
@ -475,7 +488,7 @@ Foam::sampledMeshedSurface::sampledMeshedSurface
)
:
sampledSurface(name, mesh),
MeshStorage(),
meshedSurface(),
surfaceName_(surfaceName),
surface_
(
@ -487,7 +500,9 @@ Foam::sampledMeshedSurface::sampledMeshedSurface
keepIds_(true),
zoneIds_(),
sampleElements_(),
samplePoints_()
samplePoints_(),
maxDistanceSqr_(Foam::sqr(GREAT)),
defaultValues_()
{}
@ -499,7 +514,7 @@ Foam::sampledMeshedSurface::sampledMeshedSurface
)
:
sampledSurface(name, mesh, dict),
MeshStorage(),
meshedSurface(),
surfaceName_
(
meshedSurface::findFile
@ -518,8 +533,25 @@ Foam::sampledMeshedSurface::sampledMeshedSurface
keepIds_(dict.getOrDefault("keepIds", true)),
zoneIds_(),
sampleElements_(),
samplePoints_()
samplePoints_(),
maxDistanceSqr_(Foam::sqr(GREAT)),
defaultValues_(dict.subOrEmptyDict("defaultValue"))
{
if (dict.readIfPresent("maxDistance", maxDistanceSqr_))
{
// Info<< "Limit to maxDistance " << maxDistanceSqr_ << nl;
// if (defaultValues_.empty())
// {
// Info<< "defaultValues = zero" << nl;
// }
// else
// {
// defaultValues_.writeEntry(Info);
// }
maxDistanceSqr_ = Foam::sqr(maxDistanceSqr_);
}
wordRes includePatches;
dict.readIfPresent("patches", includePatches);
includePatches.uniq();
@ -597,7 +629,7 @@ bool Foam::sampledMeshedSurface::expire()
}
sampledSurface::clearGeom();
MeshStorage::clear();
meshedSurface::clear();
zoneIds_.clear();
sampleElements_.clear();

View File

@ -35,33 +35,35 @@ Description
- 6 different modes:
- source=cells, interpolate=false:
finds per triangle centre the \em nearest cell centre and uses its value
finds per surface face centre the \em nearest cell centre
and uses its value
- source=cells, interpolate=true:
finds per triangle centre the \em nearest cell centre.
finds per surface face centre the \em nearest cell centre.
Per surface point checks if this nearest cell is the one containing
point; otherwise projects the point onto the nearest point on
the boundary of the cell (to make sure interpolateCellPoint
gets a valid location)
- source=insideCells, interpolate=false:
finds per triangle centre the cell containing it and uses its value.
Trims triangles outside mesh.
finds per surface face centre the cell containing it
and uses its value.
Trims surface faces outside of the mesh.
- source=insideCells, interpolate=true:
Per surface point interpolate cell containing it.
- source=boundaryFaces, interpolate=false:
finds per triangle centre the nearest point on the boundary
finds per surface face centre the \em nearest point on the boundary
(uncoupled faces only) and uses the value (or 0 if the nearest
is on an empty boundary)
- source=boundaryFaces, interpolate=true:
finds per triangle centre the nearest point on the boundary
finds per surface face centre the \em nearest point on the boundary
(uncoupled faces only).
Per surface point projects the point onto this boundary face
(to make sure interpolateCellPoint gets a valid location)
- since it finds a nearest per triangle each triangle is guaranteed
to be on one processor only. So after stitching (by sampledSurfaces)
the original surface should be complete.
- since it finds the nearest per surface face, each surface face
is guaranteed to be on one processor only.
So after stitching the original surface should be complete.
This is often embedded as part of a sampled surfaces function object.
@ -75,6 +77,16 @@ Usage
type meshedSurface;
surface something.obj;
source cells;
//- Max sampling distance
maxDistance 0.005;
//- Fallback for missed sampling in 'cells' mode
defaultValue
{
"p.*" 1e5;
T 273.15;
}
}
}
\endverbatim
@ -90,8 +102,11 @@ Usage
file | Alternative file name | no |
fileType | The surface format | no | (extension)
scale | Surface scaling factor | no | 0
maxDistance | Max search distance | no | GREAT
defaultValue | Value beyond max distance (dictionary) | no | empty
\endtable
SourceFiles
sampledMeshedSurface.C
sampledMeshedSurfaceTemplates.C
@ -102,8 +117,7 @@ SourceFiles
#define sampledMeshedSurface_H
#include "sampledSurface.H"
#include "MeshedSurface.H"
#include "MeshedSurfacesFwd.H"
#include "MeshedSurfaces.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -123,18 +137,19 @@ class sampledMeshedSurface
public meshedSurface
{
public:
//- Types of sampling regions
enum class samplingSource
enum class samplingSource : unsigned char
{
cells, //!< Use nearest cell value
insideCells, //!< Face within a cell
boundaryFaces //!< Use boundary face values
insideCells, //!< Surface face within a cell, or trim
boundaryFaces //!< Use nearest boundary face values
};
private:
//- Private typedefs for convenience
typedef meshedSurface MeshStorage;
//- The concrete storage type for faces and points
typedef meshedSurface Mesh;
// Private Data
@ -165,6 +180,12 @@ private:
//- Local points to sample per point
pointField samplePoints_;
//- Max search distance squared
scalar maxDistanceSqr_;
//- Out-of-range fallback values (when beyond the max search distance)
dictionary defaultValues_;
// Private Member Functions
@ -239,13 +260,13 @@ public:
//- Points of surface
virtual const pointField& points() const
{
return MeshStorage::points();
return Mesh::points();
}
//- Faces of surface
virtual const faceList& faces() const
{
return MeshStorage::surfFaces();
return Mesh::surfFaces();
}
//- Per-face zone/region information
@ -257,31 +278,31 @@ public:
//- Per-face identifier (eg, element Id)
virtual const labelList& faceIds() const
{
return MeshStorage::faceIds();
return Mesh::faceIds();
}
//- Face area vectors
virtual const vectorField& Sf() const
{
return MeshStorage::Sf();
return Mesh::Sf();
}
//- Face area magnitudes
virtual const scalarField& magSf() const
{
return MeshStorage::magSf();
return Mesh::magSf();
}
//- Face centres
virtual const vectorField& Cf() const
{
return MeshStorage::Cf();
return Mesh::Cf();
}
//- If element ids/order of the original surface are kept
virtual bool hasFaceIds() const
{
return keepIds_ && !MeshStorage::faceIds().empty();
return keepIds_ && !Mesh::faceIds().empty();
}
//- Sampling boundary values instead of cell values

View File

@ -37,8 +37,14 @@ Foam::sampledMeshedSurface::sampleOnFaces
const interpolation<Type>& sampler
) const
{
const Type deflt
(
defaultValues_.getOrDefault<Type>(sampler.psi().name(), Zero)
);
const labelList& elements = sampleElements_;
if (!onBoundary())
{
// Sample cells
@ -48,7 +54,8 @@ Foam::sampledMeshedSurface::sampleOnFaces
sampler,
elements,
faces(),
points()
points(),
deflt
);
}
@ -60,33 +67,33 @@ Foam::sampledMeshedSurface::sampleOnFaces
auto tvalues = tmp<Field<Type>>::New(elements.size());
auto& values = tvalues.ref();
const polyBoundaryMesh& pbm = mesh().boundaryMesh();
const label nBnd = mesh().nBoundaryFaces();
// Create flat boundary field
Field<Type> bVals(nBnd, Zero);
const polyBoundaryMesh& pbm = mesh().boundaryMesh();
Field<Type> bVals(mesh().nBoundaryFaces(), Zero);
const auto& bField = sampler.psi().boundaryField();
forAll(bField, patchi)
{
const label bFacei = (pbm[patchi].start() - mesh().nInternalFaces());
SubList<Type>
(
bVals,
bField[patchi].size(),
bFacei
) = bField[patchi];
SubList<Type>(bVals, pbm[patchi].range()) = bField[patchi];
}
// Sample in flat boundary field
// Sample within the flat boundary field
forAll(elements, i)
{
const label bFacei = (elements[i] - mesh().nInternalFaces());
values[i] = bVals[bFacei];
if (bFacei < 0)
{
values[i] = deflt;
}
else
{
values[i] = bVals[bFacei];
}
}
return tvalues;
@ -100,34 +107,60 @@ Foam::sampledMeshedSurface::sampleOnPoints
const interpolation<Type>& interpolator
) const
{
// One value per vertex
auto tvalues = tmp<Field<Type>>::New(sampleElements_.size());
const Type deflt
(
defaultValues_.getOrDefault<Type>(interpolator.psi().name(), Zero)
);
const labelList& elements = sampleElements_;
// One value per vertex.
// - sampleElements_ and samplePoints_ have the identical size
auto tvalues = tmp<Field<Type>>::New(elements.size());
auto& values = tvalues.ref();
if (!onBoundary())
{
// Sample cells
forAll(sampleElements_, pointi)
forAll(elements, i)
{
values[pointi] = interpolator.interpolate
(
samplePoints_[pointi],
sampleElements_[pointi]
);
const label celli = elements[i];
if (celli < 0)
{
values[i] = deflt;
}
else
{
values[i] = interpolator.interpolate
(
samplePoints_[i],
celli
);
}
}
}
else
//
// Sample boundary faces
//
forAll(elements, i)
{
// Sample boundary faces
const label facei = elements[i];
forAll(samplePoints_, pointi)
if (facei < 0)
{
const label facei = sampleElements_[pointi];
values[pointi] = interpolator.interpolate
values[i] = deflt;
}
else
{
values[i] = interpolator.interpolate
(
samplePoints_[pointi],
samplePoints_[i],
mesh().faceOwner()[facei],
facei
);

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2016-2019 OpenCFD Ltd.
Copyright (C) 2016-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -132,14 +132,16 @@ protected:
// Protected Member Functions
//- General loop for sampling elements to faces
//- General loop for sampling elements to faces.
// The defaultValue is used for invalid (negative) elements
template<class Type>
static tmp<Field<Type>> sampleOnFaces
(
const interpolation<Type>& sampler,
const labelUList& elements,
const faceList& fcs,
const pointField& pts
const pointField& pts,
const Type& defaultValue = Type(Zero)
);

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2018-2019 OpenCFD Ltd.
Copyright (C) 2018-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -37,7 +37,8 @@ Foam::sampledSurface::sampleOnFaces
const interpolation<Type>& sampler,
const labelUList& elements,
const faceList& fcs,
const pointField& pts
const pointField& pts,
const Type& defaultValue
)
{
const label len = elements.size();
@ -57,9 +58,16 @@ Foam::sampledSurface::sampleOnFaces
for (label i=0; i < len; ++i)
{
const label celli = elements[i];
const point pt = fcs[i].centre(pts);
if (celli < 0)
{
values[i] = defaultValue;
}
else
{
const point pt = fcs[i].centre(pts);
values[i] = sampler.interpolate(pt, celli);
values[i] = sampler.interpolate(pt, celli);
}
}
return tvalues;

View File

@ -121,6 +121,44 @@ sampled
}
// Sample and write for meshed surfaces
sampleMesh
{
type surfaces;
libs (sampling);
log true;
writeControl writeTime;
writeInterval 1;
surfaceFormat vtk;
fields (p rho U T);
surfaces
{
// Oversized sampling - for general testing
meshed_sample
{
type meshedSurface;
surface oversized_sample.obj;
source cells;
maxDistance 0.0025;
defaultValue
{
T 273;
}
}
meshed_interpolate
{
$meshed_sample;
interpolate true;
}
}
}
// * * * * * * * * * * * * * * * Calculations * * * * * * * * * * * * * * * //
massflow