/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2010-2011 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see .
\*---------------------------------------------------------------------------*/
#include "sampledTriSurfaceMesh.H"
#include "meshSearch.H"
#include "Tuple2.H"
#include "globalIndex.H"
#include "treeDataCell.H"
#include "treeDataFace.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(sampledTriSurfaceMesh, 0);
addToRunTimeSelectionTable
(
sampledSurface,
sampledTriSurfaceMesh,
word
);
template<>
const char* NamedEnum::names[] =
{
"cells",
"boundaryFaces"
};
const NamedEnum
sampledTriSurfaceMesh::samplingSourceNames_;
//- Private class for finding nearest
// Comprising:
// - global index
// - sqr(distance)
typedef Tuple2 nearInfo;
class nearestEqOp
{
public:
void operator()(nearInfo& x, const nearInfo& y) const
{
if (y.first() < x.first())
{
x = y;
}
}
};
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
const Foam::indexedOctree&
Foam::sampledTriSurfaceMesh::nonCoupledboundaryTree() const
{
// Variant of meshSearch::boundaryTree() that only does non-coupled
// boundary faces.
if (!boundaryTreePtr_.valid())
{
// all non-coupled boundary faces (not just walls)
const polyBoundaryMesh& patches = mesh().boundaryMesh();
labelList bndFaces(mesh().nFaces()-mesh().nInternalFaces());
label bndI = 0;
forAll(patches, patchI)
{
const polyPatch& pp = patches[patchI];
if (!pp.coupled())
{
forAll(pp, i)
{
bndFaces[bndI++] = pp.start()+i;
}
}
}
bndFaces.setSize(bndI);
treeBoundBox overallBb(mesh().points());
Random rndGen(123456);
overallBb = overallBb.extend(rndGen, 1E-4);
overallBb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
overallBb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
boundaryTreePtr_.reset
(
new indexedOctree
(
treeDataFace // all information needed to search faces
(
false, // do not cache bb
mesh(),
bndFaces // boundary faces only
),
overallBb, // overall search domain
8, // maxLevel
10, // leafsize
3.0 // duplicity
)
);
}
return boundaryTreePtr_();
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::sampledTriSurfaceMesh::sampledTriSurfaceMesh
(
const word& name,
const polyMesh& mesh,
const word& surfaceName,
const samplingSource sampleSource
)
:
sampledSurface(name, mesh),
surface_
(
IOobject
(
surfaceName,
mesh.time().constant(), // instance
"triSurface", // local
mesh, // registry
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
),
sampleSource_(sampleSource),
needsUpdate_(true),
sampleElements_(0),
samplePoints_(0)
{}
Foam::sampledTriSurfaceMesh::sampledTriSurfaceMesh
(
const word& name,
const polyMesh& mesh,
const dictionary& dict
)
:
sampledSurface(name, mesh, dict),
surface_
(
IOobject
(
dict.lookup("surface"),
mesh.time().constant(), // instance
"triSurface", // local
mesh, // registry
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
),
sampleSource_(samplingSourceNames_[dict.lookup("source")]),
needsUpdate_(true),
sampleElements_(0),
samplePoints_(0)
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::sampledTriSurfaceMesh::~sampledTriSurfaceMesh()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::sampledTriSurfaceMesh::needsUpdate() const
{
return needsUpdate_;
}
bool Foam::sampledTriSurfaceMesh::expire()
{
// already marked as expired
if (needsUpdate_)
{
return false;
}
sampledSurface::clearGeom();
MeshStorage::clear();
boundaryTreePtr_.clear();
sampleElements_.clear();
samplePoints_.clear();
needsUpdate_ = true;
return true;
}
bool Foam::sampledTriSurfaceMesh::update()
{
if (!needsUpdate_)
{
return false;
}
// Find the cells the triangles of the surface are in.
// Does approximation by looking at the face centres only
const pointField& fc = surface_.faceCentres();
// Mesh search engine, no triangulation of faces.
meshSearch meshSearcher(mesh(), false);
List nearest(fc.size());
// Global numbering for cells/faces - only used to uniquely identify local
// elements
globalIndex globalCells
(
sampleSource_ == cells
? mesh().nCells()
: mesh().nFaces()
);
forAll(nearest, i)
{
nearest[i].first() = GREAT;
nearest[i].second() = labelMax;
}
if (sampleSource_ == cells)
{
// Search for nearest cell
const indexedOctree& cellTree = meshSearcher.cellTree();
forAll(fc, triI)
{
pointIndexHit nearInfo = cellTree.findNearest
(
fc[triI],
sqr(GREAT)
);
if (nearInfo.hit())
{
nearest[triI].first() = magSqr(nearInfo.hitPoint()-fc[triI]);
nearest[triI].second() = globalCells.toGlobal(nearInfo.index());
}
}
}
else
{
// Search for nearest boundaryFace
////- Search on all (including coupled) boundary faces
//const indexedOctree& bTree = meshSearcher.boundaryTree()
//- Search on all non-coupled boundary faces
const indexedOctree& bTree = nonCoupledboundaryTree();
forAll(fc, triI)
{
pointIndexHit nearInfo = bTree.findNearest
(
fc[triI],
sqr(GREAT)
);
if (nearInfo.hit())
{
nearest[triI].first() = magSqr(nearInfo.hitPoint()-fc[triI]);
nearest[triI].second() = globalCells.toGlobal
(
bTree.shapes().faceLabels()[nearInfo.index()]
);
}
}
}
// See which processor has the nearest. Mark and subset
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pstream::listCombineGather(nearest, nearestEqOp());
Pstream::listCombineScatter(nearest);
labelList cellOrFaceLabels(fc.size(), -1);
label nFound = 0;
forAll(nearest, triI)
{
if (nearest[triI].second() == labelMax)
{
// Not found on any processor. How to map?
}
else if (globalCells.isLocal(nearest[triI].second()))
{
cellOrFaceLabels[triI] = globalCells.toLocal
(
nearest[triI].second()
);
nFound++;
}
}
if (debug)
{
Pout<< "Local out of faces:" << cellOrFaceLabels.size()
<< " keeping:" << nFound << endl;
}
// Now subset the surface. Do not use triSurface::subsetMesh since requires
// original surface to be in compact numbering.
const triSurface& s = surface_;
// Compact to original triangle
labelList faceMap(s.size());
// Compact to original points
labelList pointMap(s.points().size());
// From original point to compact points
labelList reversePointMap(s.points().size(), -1);
{
label newPointI = 0;
label newFaceI = 0;
forAll(s, faceI)
{
if (cellOrFaceLabels[faceI] != -1)
{
faceMap[newFaceI++] = faceI;
const triSurface::FaceType& f = s[faceI];
forAll(f, fp)
{
if (reversePointMap[f[fp]] == -1)
{
pointMap[newPointI] = f[fp];
reversePointMap[f[fp]] = newPointI++;
}
}
}
}
faceMap.setSize(newFaceI);
pointMap.setSize(newPointI);
}
// Subset cellOrFaceLabels
cellOrFaceLabels = UIndirectList