Lagrangian: Rewrite of the particle tracking algorithm to function in

terms of the local barycentric coordinates of the current tetrahedron,
rather than the global coordinate system.

Barycentric tracking works on any mesh, irrespective of mesh quality.
Particles do not get "lost", and tracking does not require ad-hoc
"corrections" or "rescues" to function robustly, because the calculation
of particle-face intersections is unambiguous and reproducible, even at
small angles of incidence.

Each particle position is defined by topology (i.e. the decomposed tet
cell it is in) and geometry (i.e. where it is in the cell). No search
operations are needed on restart or reconstruct, unlike when particle
positions are stored in the global coordinate system.

The particle positions file now contains particles' local coordinates
and topology, rather than the global coordinates and cell. This change
to the output format is not backwards compatible. Existing cases with
Lagrangian data will not restart, but they will still run from time
zero without any modification. This change was necessary in order to
guarantee that the loaded particle is valid, and therefore
fundamentally prevent "loss" and "search-failure" type bugs (e.g.,
2517, 2442, 2286, 1836, 1461, 1341, 1097).

The tracking functions have also been converted to function in terms
of displacement, rather than end position. This helps remove floating
point error issues, particularly towards the end of a tracking step.

Wall bounded streamlines have been removed. The implementation proved
incompatible with the new tracking algorithm. ParaView has a surface
LIC plugin which provides equivalent, or better, functionality.

Additionally, bug report <https://bugs.openfoam.org/view.php?id=2517>
is resolved by this change.
This commit is contained in:
Will Bainbridge
2017-04-28 08:03:44 +01:00
parent d2a62df7c4
commit 371762757d
99 changed files with 3081 additions and 5741 deletions

View File

@ -22,11 +22,6 @@ streamLine/streamLine.C
streamLine/streamLineParticle.C
streamLine/streamLineParticleCloud.C
wallBoundedStreamLine/wallBoundedStreamLine.C
wallBoundedStreamLine/wallBoundedStreamLineParticle.C
wallBoundedStreamLine/wallBoundedStreamLineParticleCloud.C
wallBoundedStreamLine/wallBoundedParticle.C
surfaceInterpolate/surfaceInterpolate.C
regionSizeDistribution/regionSizeDistribution.C

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2013-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2013-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -30,7 +30,7 @@ License
Foam::findCellParticle::findCellParticle
(
const polyMesh& mesh,
const vector& position,
const barycentric& coordinates,
const label celli,
const label tetFacei,
const label tetPtI,
@ -38,7 +38,24 @@ Foam::findCellParticle::findCellParticle
const label data
)
:
particle(mesh, position, celli, tetFacei, tetPtI),
particle(mesh, coordinates, celli, tetFacei, tetPtI),
start_(position()),
end_(end),
data_(data)
{}
Foam::findCellParticle::findCellParticle
(
const polyMesh& mesh,
const vector& position,
const label celli,
const point& end,
const label data
)
:
particle(mesh, position, celli),
start_(this->position()),
end_(end),
data_(data)
{}
@ -57,15 +74,15 @@ Foam::findCellParticle::findCellParticle
{
if (is.format() == IOstream::ASCII)
{
is >> end_;
is >> start_ >> end_;
data_ = readLabel(is);
}
else
{
is.read
(
reinterpret_cast<char*>(&end_),
sizeof(end_) + sizeof(data_)
reinterpret_cast<char*>(&start_),
sizeof(start_) + sizeof(end_) + sizeof(data_)
);
}
}
@ -90,21 +107,13 @@ bool Foam::findCellParticle::move
td.switchProcessor = false;
td.keepParticle = true;
scalar tEnd = (1.0 - stepFraction())*maxTrackLen;
scalar dtMax = tEnd;
while (td.keepParticle && !td.switchProcessor && tEnd > SMALL)
while (td.keepParticle && !td.switchProcessor && stepFraction() < 1)
{
// set the lagrangian time-step
scalar dt = min(dtMax, tEnd);
dt *= trackToFace(end_, td);
tEnd -= dt;
stepFraction() = 1.0 - tEnd/maxTrackLen;
const scalar f = 1 - stepFraction();
trackToFace(f*(end_ - start_), f, td);
}
if (tEnd < SMALL || !td.keepParticle)
if (stepFraction() == 1 || !td.keepParticle)
{
// Hit endpoint or patch. If patch hit could do fancy stuff but just
// to use the patch point is good enough for now.
@ -214,6 +223,7 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const findCellParticle& p)
if (os.format() == IOstream::ASCII)
{
os << static_cast<const particle&>(p)
<< token::SPACE << p.start_
<< token::SPACE << p.end_
<< token::SPACE << p.data_;
}
@ -222,8 +232,8 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const findCellParticle& p)
os << static_cast<const particle&>(p);
os.write
(
reinterpret_cast<const char*>(&p.end_),
sizeof(p.end_) + sizeof(p.data_)
reinterpret_cast<const char*>(&p.start_),
sizeof(p.start_) + sizeof(p.end_) + sizeof(p.data_)
);
}

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2013-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2013-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -63,6 +63,9 @@ class findCellParticle
{
// Private data
//- Start point to track from
point start_;
//- End point to track to
point end_;
@ -119,7 +122,7 @@ public:
findCellParticle
(
const polyMesh& mesh,
const vector& position,
const barycentric& coordinates,
const label celli,
const label tetFacei,
const label tetPtI,
@ -127,6 +130,17 @@ public:
const label data
);
//- Construct from a position and a cell, searching for the rest of the
// required topology
findCellParticle
(
const polyMesh& mesh,
const vector& position,
const label celli,
const point& end,
const label data
);
//- Construct from Istream
findCellParticle
(
@ -166,18 +180,42 @@ public:
// Member Functions
//- Point to track from
const point& start() const
{
return start_;
}
//- Point to track from
point& start()
{
return start_;
}
//- Point to track to
const point& end() const
{
return end_;
}
//- Point to track to
point& end()
{
return end_;
}
//- Transported label
label data() const
{
return data_;
}
//- Transported label
label& data()
{
return data_;
}
// Tracking

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -108,24 +108,16 @@ void Foam::functionObjects::nearWallFields::calcAddressing()
const point end = start-distance_*nf[patchFacei];
// Find tet for starting location
label celli = -1;
label tetFacei = -1;
label tetPtI = -1;
mesh_.findCellFacePt(start, celli, tetFacei, tetPtI);
// Add to cloud. Add originating face as passive data
// Add a particle to the cloud with originating face as passive data
cloud.addParticle
(
new findCellParticle
(
mesh_,
start,
celli,
tetFacei,
tetPtI,
-1,
end,
globalWalls.toGlobal(nPatchFaces) // passive data
globalWalls.toGlobal(nPatchFaces) // passive data
)
);

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -26,35 +26,8 @@ License
#include "streamLineParticle.H"
#include "vectorFieldIOField.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
// defineParticleTypeNameAndDebug(streamLineParticle, 0);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::streamLineParticle::calcSubCycleDeltaT
(
trackingData& td,
const scalar dt,
const vector& U
) const
{
particle testParticle(*this);
bool oldKeepParticle = td.keepParticle;
bool oldSwitchProcessor = td.switchProcessor;
scalar fraction = testParticle.trackToFace(position()+dt*U, td);
td.keepParticle = oldKeepParticle;
td.switchProcessor = oldSwitchProcessor;
// Adapt the dt to subdivide the trajectory into substeps.
return dt*fraction/td.nSubCycle_;
}
Foam::vector Foam::streamLineParticle::interpolateFields
(
const trackingData& td,
@ -129,7 +102,6 @@ Foam::streamLineParticle::streamLineParticle
{
if (readFields)
{
//if (is.format() == IOstream::ASCII)
List<scalarList> sampledScalars;
List<vectorList> sampledVectors;
@ -174,31 +146,22 @@ Foam::streamLineParticle::streamLineParticle
bool Foam::streamLineParticle::move
(
trackingData& td,
const scalar trackTime
const scalar
)
{
streamLineParticle& p = static_cast<streamLineParticle&>(*this);
td.switchProcessor = false;
td.keepParticle = true;
scalar tEnd = (1.0 - stepFraction())*trackTime;
scalar maxDt = mesh_.bounds().mag();
const scalar maxDt = mesh().bounds().mag();
while
(
td.keepParticle
&& !td.switchProcessor
&& lifeTime_ > 0
)
while (td.keepParticle && !td.switchProcessor && lifeTime_ > 0)
{
// set the lagrangian time-step
scalar dt = maxDt;
// Cross cell in steps:
// - at subiter 0 calculate dt to cross cell in nSubCycle steps
// - at the last subiter do all of the remaining track
for (label subIter = 0; subIter < td.nSubCycle_; subIter++)
for (label subIter = 0; subIter < max(1, td.nSubCycle_); subIter++)
{
--lifeTime_;
@ -224,37 +187,27 @@ bool Foam::streamLineParticle::move
if (td.trackLength_ < GREAT)
{
// No sub-cycling. Track a set length on each step.
dt = td.trackLength_;
//Pout<< " subiteration " << subIter
// << " : fixed length: updated dt:" << dt << endl;
}
else if (subIter == 0 && td.nSubCycle_ > 1)
else if (subIter == 0)
{
// Adapt dt to cross cell in a few steps
dt = calcSubCycleDeltaT(td, dt, U);
// Sub-cycling. Cross the cell in nSubCycle steps.
particle copy(*this);
copy.trackToFace(maxDt*U, 1);
dt *= (copy.stepFraction() - stepFraction())/td.nSubCycle_;
}
else if (subIter == td.nSubCycle_ - 1)
{
// Do full step on last subcycle
// Sub-cycling. Track the whole cell on the last step.
dt = maxDt;
}
scalar fraction = trackToFace(position() + dt*U, td);
dt *= fraction;
tEnd -= dt;
stepFraction() = 1.0 - tEnd/trackTime;
if (tEnd <= ROOTVSMALL)
{
// Force removal
lifeTime_ = 0;
}
trackToFace(dt*U, 0, td);
if
(
face() != -1
onFace()
|| !td.keepParticle
|| td.switchProcessor
|| lifeTime_ == 0
@ -265,17 +218,16 @@ bool Foam::streamLineParticle::move
}
}
if (!td.keepParticle || lifeTime_ == 0)
{
if (lifeTime_ == 0)
{
// Failure exit. Particle stagnated or it's life ran out.
if (debug)
{
Pout<< "streamLineParticle : Removing stagnant particle:"
<< p.position()
<< " sampled positions:" << sampledPositions_.size()
<< endl;
Pout<< "streamLineParticle: Removing stagnant particle:"
<< position() << " sampled positions:"
<< sampledPositions_.size() << endl;
}
td.keepParticle = false;
}
@ -287,29 +239,25 @@ bool Foam::streamLineParticle::move
if (debug)
{
Pout<< "streamLineParticle : Removing particle:"
<< p.position()
Pout<< "streamLineParticle: Removing particle:" << position()
<< " sampled positions:" << sampledPositions_.size()
<< endl;
}
}
// Transfer particle data into trackingData.
//td.allPositions_.append(sampledPositions_);
td.allPositions_.append(vectorList());
vectorList& top = td.allPositions_.last();
top.transfer(sampledPositions_);
forAll(sampledScalars_, i)
{
//td.allScalars_[i].append(sampledScalars_[i]);
td.allScalars_[i].append(scalarList());
scalarList& top = td.allScalars_[i].last();
top.transfer(sampledScalars_[i]);
}
forAll(sampledVectors_, i)
{
//td.allVectors_[i].append(sampledVectors_[i]);
td.allVectors_[i].append(vectorList());
vectorList& top = td.allVectors_[i].last();
top.transfer(sampledVectors_[i]);
@ -433,18 +381,11 @@ void Foam::streamLineParticle::readFields(Cloud<streamLineParticle>& c)
);
c.checkFieldIOobject(c, sampledPositions);
// vectorFieldIOField sampleVelocity
// (
// c.fieldIOobject("sampleVelocity", IOobject::MUST_READ)
// );
// c.checkFieldIOobject(c, sampleVelocity);
label i = 0;
forAllIter(Cloud<streamLineParticle>, c, iter)
{
iter().lifeTime_ = lifeTime[i];
iter().sampledPositions_.transfer(sampledPositions[i]);
// iter().sampleVelocity_.transfer(sampleVelocity[i]);
i++;
}
}
@ -466,24 +407,17 @@ void Foam::streamLineParticle::writeFields(const Cloud<streamLineParticle>& c)
c.fieldIOobject("sampledPositions", IOobject::NO_READ),
np
);
// vectorFieldIOField sampleVelocity
// (
// c.fieldIOobject("sampleVelocity", IOobject::NO_READ),
// np
// );
label i = 0;
forAllConstIter(Cloud<streamLineParticle>, c, iter)
{
lifeTime[i] = iter().lifeTime_;
sampledPositions[i] = iter().sampledPositions_;
// sampleVelocity[i] = iter().sampleVelocity_;
i++;
}
lifeTime.write();
sampledPositions.write();
// sampleVelocity.write();
}

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -46,16 +46,12 @@ SourceFiles
namespace Foam
{
class streamLineParticleCloud;
// Forward declaration of friend functions and operators
class streamLineParticle;
Ostream& operator<<(Ostream&, const streamLineParticle&);
/*---------------------------------------------------------------------------*\
Class streamLineParticle Declaration
\*---------------------------------------------------------------------------*/
@ -64,32 +60,38 @@ class streamLineParticle
:
public particle
{
public:
//- Class used to pass tracking data to the trackToFace function
class trackingData
:
public particle::TrackingData<Cloud<streamLineParticle>>
{
public:
// Public data
const PtrList<interpolation<scalar>>& vsInterp_;
const PtrList<interpolation<vector>>& vvInterp_;
const label UIndex_;
const bool trackForward_;
const label nSubCycle_;
const scalar trackLength_;
const PtrList<interpolation<scalar>>& vsInterp_;
DynamicList<vectorList>& allPositions_;
List<DynamicList<scalarList>>& allScalars_;
List<DynamicList<vectorList>>& allVectors_;
const PtrList<interpolation<vector>>& vvInterp_;
const label UIndex_;
const bool trackForward_;
const label nSubCycle_;
const scalar trackLength_;
DynamicList<vectorList>& allPositions_;
List<DynamicList<scalarList>>& allScalars_;
List<DynamicList<vectorList>>& allVectors_;
// Constructors
//- Construct from components
trackingData
(
Cloud<streamLineParticle>& cloud,
@ -99,7 +101,6 @@ public:
const bool trackForward,
const label nSubCycle,
const scalar trackLength,
DynamicList<List<point>>& allPositions,
List<DynamicList<scalarList>>& allScalars,
List<DynamicList<vectorList>>& allVectors
@ -112,7 +113,6 @@ public:
trackForward_(trackForward),
nSubCycle_(nSubCycle),
trackLength_(trackLength),
allPositions_(allPositions),
allScalars_(allScalars),
allVectors_(allVectors)
@ -139,22 +139,6 @@ private:
// Private Member Functions
//- Estimate dt to cross from current face to next one in nSubCycle
// steps.
scalar calcSubCycleDeltaT
(
trackingData& td,
const scalar dt,
const vector& U
) const;
void constrainVelocity
(
trackingData& td,
const scalar dt,
vector& U
);
//- Interpolate all quantities; return interpolated velocity.
vector interpolateFields
(
@ -195,8 +179,7 @@ public:
return autoPtr<particle>(new streamLineParticle(*this));
}
//- Factory class to read-construct particles used for
// parallel transfer
//- Factory class to read-construct particles used for parallel transfer
class iNew
{
const polyMesh& mesh_;
@ -223,8 +206,7 @@ public:
// Tracking
//- Track all particles to their end point
bool move(trackingData&, const scalar trackTime);
bool move(trackingData&, const scalar);
//- Overridable function to handle the particle hitting a patch
// Executed before other patch-hitting functions

View File

@ -1,425 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "wallBoundedParticle.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const std::size_t Foam::wallBoundedParticle::sizeofFields_
(
sizeof(wallBoundedParticle) - sizeof(particle)
);
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
Foam::edge Foam::wallBoundedParticle::currentEdge() const
{
if ((meshEdgeStart_ != -1) == (diagEdge_ != -1))
{
FatalErrorInFunction
<< "Particle:"
<< info()
<< "cannot both be on a mesh edge and a face-diagonal edge."
<< " meshEdgeStart_:" << meshEdgeStart_
<< " diagEdge_:" << diagEdge_
<< abort(FatalError);
}
const Foam::face& f = mesh_.faces()[tetFace()];
if (meshEdgeStart_ != -1)
{
return edge(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_));
}
else
{
label faceBasePtI = mesh_.tetBasePtIs()[tetFace()];
label diagPtI = (faceBasePtI+diagEdge_)%f.size();
return edge(f[faceBasePtI], f[diagPtI]);
}
}
void Foam::wallBoundedParticle::crossEdgeConnectedFace
(
const edge& meshEdge
)
{
// Update tetFace, tetPt
particle::crossEdgeConnectedFace(cell(), tetFace(), tetPt(), meshEdge);
// Update face to be same as tracking one
face() = tetFace();
// And adapt meshEdgeStart_.
const Foam::face& f = mesh_.faces()[tetFace()];
label fp = findIndex(f, meshEdge[0]);
if (f.nextLabel(fp) == meshEdge[1])
{
meshEdgeStart_ = fp;
}
else
{
label fpMin1 = f.rcIndex(fp);
if (f[fpMin1] == meshEdge[1])
{
meshEdgeStart_ = fpMin1;
}
else
{
FatalErrorInFunction
<< "Problem :"
<< " particle:"
<< info()
<< "face:" << tetFace()
<< " verts:" << f
<< " meshEdge:" << meshEdge
<< abort(FatalError);
}
}
diagEdge_ = -1;
// Check that still on same mesh edge
const edge eNew(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_));
if (eNew != meshEdge)
{
FatalErrorInFunction
<< "Problem" << abort(FatalError);
}
}
void Foam::wallBoundedParticle::crossDiagonalEdge()
{
if (diagEdge_ == -1)
{
FatalErrorInFunction
<< "Particle:"
<< info()
<< "not on a diagonal edge" << abort(FatalError);
}
if (meshEdgeStart_ != -1)
{
FatalErrorInFunction
<< "Particle:"
<< info()
<< "meshEdgeStart_:" << meshEdgeStart_ << abort(FatalError);
}
const Foam::face& f = mesh_.faces()[tetFace()];
// tetPtI starts from 1, goes up to f.size()-2
if (tetPt() == diagEdge_)
{
tetPt() = f.rcIndex(tetPt());
}
else
{
label nextTetPt = f.fcIndex(tetPt());
if (diagEdge_ == nextTetPt)
{
tetPt() = nextTetPt;
}
else
{
FatalErrorInFunction
<< "Particle:"
<< info()
<< "tetPt:" << tetPt()
<< " diagEdge:" << diagEdge_ << abort(FatalError);
}
}
meshEdgeStart_ = -1;
}
Foam::scalar Foam::wallBoundedParticle::trackFaceTri
(
const vector& endPosition,
label& minEdgeI
)
{
// Track p from position to endPosition
const triFace tri(currentTetIndices().faceTriIs(mesh_));
vector n = tri.normal(mesh_.points());
n /= mag(n)+VSMALL;
// Check which edge intersects the trajectory.
// Project trajectory onto triangle
minEdgeI = -1;
scalar minS = 1; // end position
edge currentE(-1, -1);
if (meshEdgeStart_ != -1 || diagEdge_ != -1)
{
currentE = currentEdge();
}
// Determine path along line position+s*d to see where intersections
// are.
forAll(tri, i)
{
label j = tri.fcIndex(i);
const point& pt0 = mesh_.points()[tri[i]];
const point& pt1 = mesh_.points()[tri[j]];
if (edge(tri[i], tri[j]) == currentE)
{
// Do not check particle is on
continue;
}
// Outwards pointing normal
vector edgeNormal = (pt1-pt0)^n;
edgeNormal /= mag(edgeNormal)+VSMALL;
// Determine whether position and end point on either side of edge.
scalar sEnd = (endPosition-pt0)&edgeNormal;
if (sEnd >= 0)
{
// endPos is outside triangle. position() should always be
// inside.
scalar sStart = (position()-pt0)&edgeNormal;
if (mag(sEnd-sStart) > VSMALL)
{
scalar s = sStart/(sStart-sEnd);
if (s >= 0 && s < minS)
{
minS = s;
minEdgeI = i;
}
}
}
}
if (minEdgeI != -1)
{
position() += minS*(endPosition-position());
}
else
{
// Did not hit any edge so tracked to the end position. Set position
// without any calculation to avoid truncation errors.
position() = endPosition;
minS = 1.0;
}
// Project position() back onto plane of triangle
const point& triPt = mesh_.points()[tri[0]];
position() -= ((position()-triPt)&n)*n;
return minS;
}
bool Foam::wallBoundedParticle::isTriAlongTrack
(
const point& endPosition
) const
{
const triFace triVerts(currentTetIndices().faceTriIs(mesh_));
const edge currentE = currentEdge();
//if (debug)
{
if
(
currentE[0] == currentE[1]
|| findIndex(triVerts, currentE[0]) == -1
|| findIndex(triVerts, currentE[1]) == -1
)
{
FatalErrorInFunction
<< "Edge " << currentE << " not on triangle " << triVerts
<< info()
<< abort(FatalError);
}
}
const vector dir = endPosition-position();
// Get normal of currentE
vector n = triVerts.normal(mesh_.points());
n /= mag(n);
forAll(triVerts, i)
{
label j = triVerts.fcIndex(i);
const point& pt0 = mesh_.points()[triVerts[i]];
const point& pt1 = mesh_.points()[triVerts[j]];
if (edge(triVerts[i], triVerts[j]) == currentE)
{
vector edgeNormal = (pt1-pt0)^n;
return (dir&edgeNormal) < 0;
}
}
FatalErrorInFunction
<< "Problem" << abort(FatalError);
return false;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::wallBoundedParticle::wallBoundedParticle
(
const polyMesh& mesh,
const vector& position,
const label celli,
const label tetFacei,
const label tetPtI,
const label meshEdgeStart,
const label diagEdge
)
:
particle(mesh, position, celli, tetFacei, tetPtI),
meshEdgeStart_(meshEdgeStart),
diagEdge_(diagEdge)
{}
Foam::wallBoundedParticle::wallBoundedParticle
(
const polyMesh& mesh,
Istream& is,
bool readFields
)
:
particle(mesh, is, readFields)
{
if (readFields)
{
if (is.format() == IOstream::ASCII)
{
is >> meshEdgeStart_ >> diagEdge_;
}
else
{
is.read
(
reinterpret_cast<char*>(&meshEdgeStart_),
sizeof(meshEdgeStart_)
+ sizeof(diagEdge_)
);
}
}
// Check state of Istream
is.check
(
"wallBoundedParticle::wallBoundedParticle"
"(const Cloud<wallBoundedParticle>&, Istream&, bool)"
);
}
Foam::wallBoundedParticle::wallBoundedParticle
(
const wallBoundedParticle& p
)
:
particle(p),
meshEdgeStart_(p.meshEdgeStart_),
diagEdge_(p.diagEdge_)
{}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const wallBoundedParticle& p
)
{
if (os.format() == IOstream::ASCII)
{
os << static_cast<const particle&>(p)
<< token::SPACE << p.meshEdgeStart_
<< token::SPACE << p.diagEdge_;
}
else
{
os << static_cast<const particle&>(p);
os.write
(
reinterpret_cast<const char*>(&p.meshEdgeStart_),
wallBoundedParticle::sizeofFields_
);
}
return os;
}
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const InfoProxy<wallBoundedParticle>& ip
)
{
const wallBoundedParticle& p = ip.t_;
tetPointRef tpr(p.currentTet());
os << " " << static_cast<const particle&>(p) << nl
<< " tet:" << nl;
os << " ";
meshTools::writeOBJ(os, tpr.a());
os << " ";
meshTools::writeOBJ(os, tpr.b());
os << " ";
meshTools::writeOBJ(os, tpr.c());
os << " ";
meshTools::writeOBJ(os, tpr.d());
os << " l 1 2" << nl
<< " l 1 3" << nl
<< " l 1 4" << nl
<< " l 2 3" << nl
<< " l 2 4" << nl
<< " l 3 4" << nl;
os << " ";
meshTools::writeOBJ(os, p.position());
return os;
}
// ************************************************************************* //

View File

@ -1,364 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
Class
Foam::wallBoundedParticle
Description
Particle class that tracks on triangles of boundary faces. Use
trackToEdge similar to trackToFace on particle.
SourceFiles
wallBoundedParticle.C
wallBoundedParticleTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef wallBoundedParticle_H
#define wallBoundedParticle_H
#include "particle.H"
#include "autoPtr.H"
#include "InfoProxy.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of friend functions and operators
class wallBoundedParticle;
Ostream& operator<<(Ostream&, const wallBoundedParticle&);
Ostream& operator<<(Ostream&, const InfoProxy<wallBoundedParticle>&);
/*---------------------------------------------------------------------------*\
Class wallBoundedParticle Declaration
\*---------------------------------------------------------------------------*/
class wallBoundedParticle
:
public particle
{
// Private data
//- Size in bytes of the fields
static const std::size_t sizeofFields_;
public:
//- Class used to pass tracking data to the trackToFace function
template<class CloudType>
class TrackingData
:
public particle::TrackingData<CloudType>
{
public:
const PackedBoolList& isWallPatch_;
// Constructors
inline TrackingData
(
CloudType& cloud,
const PackedBoolList& isWallPatch
)
:
particle::TrackingData<CloudType>
(
cloud
),
isWallPatch_(isWallPatch)
{}
};
protected:
// Protected data
//- Particle is on mesh edge:
// const face& f = mesh.faces()[tetFace()]
// const edge e(f[meshEdgeStart_], f.nextLabel(meshEdgeStart_));
// Note that this real edge
// is also one of the edges of the face-triangle (from
// tetFace()+tetPt()).
label meshEdgeStart_;
//- Particle is on diagonal edge:
// const face& f = mesh.faces()[tetFace()]
// label faceBasePtI = mesh.tetBasePtIs()[facei];
// label diagPtI = (faceBasePtI+diagEdge_)%f.size();
// const edge e(f[faceBasePtI], f[diagPtI]);
label diagEdge_;
// Protected Member Functions
//- Construct current edge
edge currentEdge() const;
//- Check if inside current tet
//void checkInside() const;
//- Check if on current edge
//void checkOnEdge() const;
//- Check if point on triangle
//void checkOnTriangle(const point&) const;
//- Cross mesh edge into different face on same cell
void crossEdgeConnectedFace(const edge& meshEdge);
//- Cross diagonal edge into different triangle on same face,cell
void crossDiagonalEdge();
//- Track through single triangle
scalar trackFaceTri(const vector& endPosition, label& minEdgeI);
//- Is current triangle in the track direction
bool isTriAlongTrack(const point& endPosition) const;
// Patch interactions
//- Do all patch interaction
template<class TrackData>
void patchInteraction(TrackData& td, const scalar trackFraction);
//- Overridable function to handle the particle hitting a patch
// Executed before other patch-hitting functions
template<class TrackData>
bool hitPatch
(
const polyPatch&,
TrackData& td,
const label patchi,
const scalar trackFraction,
const tetIndices& tetIs
);
//- Overridable function to handle the particle hitting a wedge
template<class TrackData>
void hitWedgePatch
(
const wedgePolyPatch&,
TrackData& td
);
//- Overridable function to handle the particle hitting a
// symmetry plane
template<class TrackData>
void hitSymmetryPlanePatch
(
const symmetryPlanePolyPatch&,
TrackData& td
);
//- Overridable function to handle the particle hitting a
// symmetry patch
template<class TrackData>
void hitSymmetryPatch
(
const symmetryPolyPatch&,
TrackData& td
);
//- Overridable function to handle the particle hitting a cyclic
template<class TrackData>
void hitCyclicPatch
(
const cyclicPolyPatch&,
TrackData& td
);
//- Overridable function to handle the particle hitting a
//- processorPatch
template<class TrackData>
void hitProcessorPatch
(
const processorPolyPatch&,
TrackData& td
);
//- Overridable function to handle the particle hitting a wallPatch
template<class TrackData>
void hitWallPatch
(
const wallPolyPatch&,
TrackData& td,
const tetIndices&
);
//- Overridable function to handle the particle hitting a polyPatch
template<class TrackData>
void hitPatch
(
const polyPatch&,
TrackData& td
);
public:
// Constructors
//- Construct from components
wallBoundedParticle
(
const polyMesh& c,
const vector& position,
const label celli,
const label tetFacei,
const label tetPtI,
const label meshEdgeStart,
const label diagEdge
);
//- Construct from Istream
wallBoundedParticle
(
const polyMesh& c,
Istream& is,
bool readFields = true
);
//- Construct copy
wallBoundedParticle(const wallBoundedParticle& p);
//- Construct and return a clone
autoPtr<particle> clone() const
{
return autoPtr<particle>(new wallBoundedParticle(*this));
}
//- Factory class to read-construct particles used for
// parallel transfer
class iNew
{
const polyMesh& mesh_;
public:
iNew(const polyMesh& mesh)
:
mesh_(mesh)
{}
autoPtr<wallBoundedParticle> operator()
(
Istream& is
) const
{
return autoPtr<wallBoundedParticle>
(
new wallBoundedParticle(mesh_, is, true)
);
}
};
// Member Functions
// Access
//- -1 or label of mesh edge
inline label meshEdgeStart() const
{
return meshEdgeStart_;
}
//- -1 or diagonal edge
inline label diagEdge() const
{
return diagEdge_;
}
// Track
//- Equivalent of trackToFace
template<class TrackData>
scalar trackToEdge
(
TrackData& td,
const vector& endPosition
);
// Info
//- Return info proxy.
// Used to print particle information to a stream
inline InfoProxy<wallBoundedParticle> info() const
{
return *this;
}
// I-O
//- Read
template<class CloudType>
static void readFields(CloudType&);
//- Write
template<class CloudType>
static void writeFields(const CloudType&);
// Ostream Operator
friend Ostream& operator<<
(
Ostream&,
const wallBoundedParticle&
);
friend Ostream& operator<<
(
Ostream&,
const InfoProxy<wallBoundedParticle>&
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "wallBoundedParticleTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,559 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "wallBoundedParticle.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class TrackData>
void Foam::wallBoundedParticle::patchInteraction
(
TrackData& td,
const scalar trackFraction
)
{
typedef typename TrackData::cloudType::particleType particleType;
particleType& p = static_cast<particleType&>(*this);
p.hitFace(td);
if (!internalFace(facei_))
{
label origFacei = facei_;
label patchi = patch(facei_);
// No action taken for tetPti_ for tetFacei_ here, handled by
// patch interaction call or later during processor transfer.
// Dummy tet indices. What to do here?
tetIndices faceHitTetIs;
if
(
!p.hitPatch
(
mesh_.boundaryMesh()[patchi],
td,
patchi,
trackFraction,
faceHitTetIs
)
)
{
// Did patch interaction model switch patches?
// Note: recalculate meshEdgeStart_, diagEdge_!
if (facei_ != origFacei)
{
patchi = patch(facei_);
}
const polyPatch& patch = mesh_.boundaryMesh()[patchi];
if (isA<wedgePolyPatch>(patch))
{
p.hitWedgePatch
(
static_cast<const wedgePolyPatch&>(patch), td
);
}
else if (isA<symmetryPlanePolyPatch>(patch))
{
p.hitSymmetryPlanePatch
(
static_cast<const symmetryPlanePolyPatch&>(patch), td
);
}
else if (isA<symmetryPolyPatch>(patch))
{
p.hitSymmetryPatch
(
static_cast<const symmetryPolyPatch&>(patch), td
);
}
else if (isA<cyclicPolyPatch>(patch))
{
p.hitCyclicPatch
(
static_cast<const cyclicPolyPatch&>(patch), td
);
}
else if (isA<processorPolyPatch>(patch))
{
p.hitProcessorPatch
(
static_cast<const processorPolyPatch&>(patch), td
);
}
else if (isA<wallPolyPatch>(patch))
{
p.hitWallPatch
(
static_cast<const wallPolyPatch&>(patch), td, faceHitTetIs
);
}
else
{
p.hitPatch(patch, td);
}
}
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class TrackData>
Foam::scalar Foam::wallBoundedParticle::trackToEdge
(
TrackData& td,
const vector& endPosition
)
{
// Track particle to a given position and returns 1.0 if the
// trajectory is completed without hitting a face otherwise
// stops at the face and returns the fraction of the trajectory
// completed.
// on entry 'stepFraction()' should be set to the fraction of the
// time-step at which the tracking starts.
// Are we on a track face? If not we do a topological walk.
// Particle:
// - cell_ always set
// - tetFace_, tetPt_ always set (these identify tet particle is in)
// - optionally meshEdgeStart_ or diagEdge_ set (edge particle is on)
//checkInside();
//checkOnTriangle(position());
//if (meshEdgeStart_ != -1 || diagEdge_ != -1)
//{
// checkOnEdge();
//}
scalar trackFraction = 0.0;
if (!td.isWallPatch_[tetFace()])
{
// Don't track across face. Just walk in cell. Particle is on
// mesh edge (as indicated by meshEdgeStart_).
const edge meshEdge(currentEdge());
// If internal face check whether to go to neighbour cell or just
// check to the other internal tet on the edge.
if (mesh_.isInternalFace(tetFace()))
{
label nbrCelli =
(
celli_ == mesh_.faceOwner()[facei_]
? mesh_.faceNeighbour()[facei_]
: mesh_.faceOwner()[facei_]
);
// Check angle to nbrCell tet. Is it in the direction of the
// endposition? I.e. since volume of nbr tet is positive the
// tracking direction should be into the tet.
tetIndices nbrTi(nbrCelli, tetFacei_, tetPti_, mesh_);
if ((nbrTi.faceTri(mesh_).normal() & (endPosition-position())) < 0)
{
// Change into nbrCell. No need to change tetFace, tetPt.
//Pout<< " crossed from cell:" << celli_
// << " into " << nbrCelli << endl;
celli_ = nbrCelli;
patchInteraction(td, trackFraction);
}
else
{
// Walk to other face on edge. Changes tetFace, tetPt but not
// cell.
crossEdgeConnectedFace(meshEdge);
patchInteraction(td, trackFraction);
}
}
else
{
// Walk to other face on edge. This might give loop since
// particle should have been removed?
crossEdgeConnectedFace(meshEdge);
patchInteraction(td, trackFraction);
}
}
else
{
// We're inside a tet on the wall. Check if the current tet is
// the one to cross. If not we cross into the neighbouring triangle.
if (mesh_.isInternalFace(tetFace()))
{
FatalErrorInFunction
<< "Can only track on boundary faces."
<< " Face:" << tetFace()
<< " at:" << mesh_.faceCentres()[tetFace()]
<< abort(FatalError);
}
point projectedEndPosition = endPosition;
// Remove normal component
{
const triFace tri(currentTetIndices().faceTriIs(mesh_));
vector n = tri.normal(mesh_.points());
n /= mag(n);
const point& basePt = mesh_.points()[tri[0]];
projectedEndPosition -= ((projectedEndPosition-basePt)&n)*n;
}
bool doTrack = false;
if (meshEdgeStart_ == -1 && diagEdge_ == -1)
{
// We're starting and not yet on an edge.
doTrack = true;
}
else
{
// See if the current triangle has got a point on the
// correct side of the edge.
doTrack = isTriAlongTrack(projectedEndPosition);
}
if (doTrack)
{
// Track across triangle. Return triangle edge crossed.
label triEdgeI = -1;
trackFraction = trackFaceTri(projectedEndPosition, triEdgeI);
if (triEdgeI == -1)
{
// Reached endpoint
//checkInside();
diagEdge_ = -1;
meshEdgeStart_ = -1;
return trackFraction;
}
const tetIndices ti(currentTetIndices());
// Triangle (faceTriIs) gets constructed from
// f[faceBasePtI_],
// f[facePtAI_],
// f[facePtBI_]
//
// So edge indices are:
// 0 : edge between faceBasePtI_ and facePtAI_
// 1 : edge between facePtAI_ and facePtBI_ (is always a real edge)
// 2 : edge between facePtBI_ and faceBasePtI_
const Foam::face& f = mesh_.faces()[ti.face()];
const label fp0 = ti.faceBasePt();
if (triEdgeI == 0)
{
if (ti.facePtA() == f.fcIndex(fp0))
{
//Pout<< "Real edge." << endl;
diagEdge_ = -1;
meshEdgeStart_ = fp0;
//checkOnEdge();
crossEdgeConnectedFace(currentEdge());
patchInteraction(td, trackFraction);
}
else if (ti.facePtA() == f.rcIndex(fp0))
{
//Note: should not happen since boundary face so owner
//Pout<< "Real edge." << endl;
FatalErrorInFunction
<< abort(FatalError);
diagEdge_ = -1;
meshEdgeStart_ = f.rcIndex(fp0);
//checkOnEdge();
crossEdgeConnectedFace(currentEdge());
patchInteraction(td, trackFraction);
}
else
{
// Get index of triangle on other side of edge.
diagEdge_ = ti.facePtA()-fp0;
if (diagEdge_ < 0)
{
diagEdge_ += f.size();
}
meshEdgeStart_ = -1;
//checkOnEdge();
crossDiagonalEdge();
}
}
else if (triEdgeI == 1)
{
//Pout<< "Real edge." << endl;
diagEdge_ = -1;
meshEdgeStart_ = ti.facePtA();
//checkOnEdge();
crossEdgeConnectedFace(currentEdge());
patchInteraction(td, trackFraction);
}
else // if (triEdgeI == 2)
{
if (ti.facePtB() == f.rcIndex(fp0))
{
//Pout<< "Real edge." << endl;
diagEdge_ = -1;
meshEdgeStart_ = ti.facePtB();
//checkOnEdge();
crossEdgeConnectedFace(currentEdge());
patchInteraction(td, trackFraction);
}
else if (ti.facePtB() == f.fcIndex(fp0))
{
//Note: should not happen since boundary face so owner
//Pout<< "Real edge." << endl;
FatalErrorInFunction
<< abort(FatalError);
diagEdge_ = -1;
meshEdgeStart_ = fp0;
//checkOnEdge();
crossEdgeConnectedFace(currentEdge());
patchInteraction(td, trackFraction);
}
else
{
//Pout<< "Triangle edge." << endl;
// Get index of triangle on other side of edge.
diagEdge_ = ti.facePtB()-fp0;
if (diagEdge_ < 0)
{
diagEdge_ += f.size();
}
meshEdgeStart_ = -1;
//checkOnEdge();
crossDiagonalEdge();
}
}
}
else
{
// Current tet is not the right one. Check the neighbour tet.
if (meshEdgeStart_ != -1)
{
// Particle is on mesh edge so change into other face on cell
crossEdgeConnectedFace(currentEdge());
//checkOnEdge();
patchInteraction(td, trackFraction);
}
else
{
// Particle is on diagonal edge so change into the other
// triangle.
crossDiagonalEdge();
//checkOnEdge();
}
}
}
//checkInside();
return trackFraction;
}
template<class TrackData>
bool Foam::wallBoundedParticle::hitPatch
(
const polyPatch&,
TrackData& td,
const label patchi,
const scalar trackFraction,
const tetIndices& tetIs
)
{
// Disable generic patch interaction
return false;
}
template<class TrackData>
void Foam::wallBoundedParticle::hitWedgePatch
(
const wedgePolyPatch& pp,
TrackData& td
)
{
// Remove particle
td.keepParticle = false;
}
template<class TrackData>
void Foam::wallBoundedParticle::hitSymmetryPlanePatch
(
const symmetryPlanePolyPatch& pp,
TrackData& td
)
{
// Remove particle
td.keepParticle = false;
}
template<class TrackData>
void Foam::wallBoundedParticle::hitSymmetryPatch
(
const symmetryPolyPatch& pp,
TrackData& td
)
{
// Remove particle
td.keepParticle = false;
}
template<class TrackData>
void Foam::wallBoundedParticle::hitCyclicPatch
(
const cyclicPolyPatch& pp,
TrackData& td
)
{
// Remove particle
td.keepParticle = false;
}
template<class TrackData>
void Foam::wallBoundedParticle::hitProcessorPatch
(
const processorPolyPatch& pp,
TrackData& td
)
{
// Switch particle
td.switchProcessor = true;
// Adapt edgeStart_ for other side.
// E.g. if edgeStart_ is 1 then the edge is between vertex 1 and 2 so
// on the other side between 2 and 3 so edgeStart_ should be
// f.size()-edgeStart_-1.
const Foam::face& f = mesh_.faces()[face()];
if (meshEdgeStart_ != -1)
{
meshEdgeStart_ = f.size()-meshEdgeStart_-1;
}
else
{
// diagEdge_ is relative to faceBasePt
diagEdge_ = f.size()-diagEdge_;
}
}
template<class TrackData>
void Foam::wallBoundedParticle::hitWallPatch
(
const wallPolyPatch& wpp,
TrackData& td,
const tetIndices&
)
{}
template<class TrackData>
void Foam::wallBoundedParticle::hitPatch
(
const polyPatch& wpp,
TrackData& td
)
{
// Remove particle
td.keepParticle = false;
}
template<class CloudType>
void Foam::wallBoundedParticle::readFields(CloudType& c)
{
if (!c.size())
{
return;
}
particle::readFields(c);
IOField<label> meshEdgeStart
(
c.fieldIOobject("meshEdgeStart", IOobject::MUST_READ)
);
IOField<label> diagEdge
(
c.fieldIOobject("diagEdge_", IOobject::MUST_READ)
);
c.checkFieldIOobject(c, diagEdge);
label i = 0;
forAllIter(typename CloudType, c, iter)
{
iter().meshEdgeStart_ = meshEdgeStart[i];
iter().diagEdge_ = diagEdge[i];
i++;
}
}
template<class CloudType>
void Foam::wallBoundedParticle::writeFields(const CloudType& c)
{
particle::writeFields(c);
label np = c.size();
IOField<label> meshEdgeStart
(
c.fieldIOobject("meshEdgeStart", IOobject::NO_READ),
np
);
IOField<label> diagEdge
(
c.fieldIOobject("diagEdge", IOobject::NO_READ),
np
);
label i = 0;
forAllConstIter(typename CloudType, c, iter)
{
meshEdgeStart[i] = iter().meshEdgeStart_;
diagEdge[i] = iter().diagEdge_;
i++;
}
meshEdgeStart.write();
diagEdge.write();
}
// ************************************************************************* //

View File

@ -1,824 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "Pstream.H"
#include "functionObjectList.H"
#include "wallBoundedStreamLine.H"
#include "fvMesh.H"
#include "wallBoundedStreamLineParticleCloud.H"
#include "ReadFields.H"
#include "meshSearch.H"
#include "sampledSet.H"
#include "globalIndex.H"
#include "mapDistribute.H"
#include "interpolationCellPoint.H"
#include "PatchTools.H"
#include "meshSearchMeshObject.H"
#include "faceSet.H"
#include "mapPolyMesh.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(wallBoundedStreamLine, 0);
addToRunTimeSelectionTable
(
functionObject,
wallBoundedStreamLine,
dictionary
);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::autoPtr<Foam::indirectPrimitivePatch>
Foam::functionObjects::wallBoundedStreamLine::wallPatch() const
{
const polyBoundaryMesh& patches = mesh_.boundaryMesh();
label nFaces = 0;
forAll(patches, patchi)
{
//if (!polyPatch::constraintType(patches[patchi].type()))
if (isA<wallPolyPatch>(patches[patchi]))
{
nFaces += patches[patchi].size();
}
}
labelList addressing(nFaces);
nFaces = 0;
forAll(patches, patchi)
{
//if (!polyPatch::constraintType(patches[patchi].type()))
if (isA<wallPolyPatch>(patches[patchi]))
{
const polyPatch& pp = patches[patchi];
forAll(pp, i)
{
addressing[nFaces++] = pp.start()+i;
}
}
}
return autoPtr<indirectPrimitivePatch>
(
new indirectPrimitivePatch
(
IndirectList<face>
(
mesh_.faces(),
addressing
),
mesh_.points()
)
);
}
Foam::tetIndices Foam::functionObjects::wallBoundedStreamLine::findNearestTet
(
const PackedBoolList& isWallPatch,
const point& seedPt,
const label celli
) const
{
const cell& cFaces = mesh_.cells()[celli];
label minFacei = -1;
label minTetPtI = -1;
scalar minDistSqr = sqr(GREAT);
forAll(cFaces, cFacei)
{
label facei = cFaces[cFacei];
if (isWallPatch[facei])
{
const face& f = mesh_.faces()[facei];
const label fp0 = mesh_.tetBasePtIs()[facei];
const point& basePoint = mesh_.points()[f[fp0]];
label fp = f.fcIndex(fp0);
for (label i = 2; i < f.size(); i++)
{
const point& thisPoint = mesh_.points()[f[fp]];
label nextFp = f.fcIndex(fp);
const point& nextPoint = mesh_.points()[f[nextFp]];
const triPointRef tri(basePoint, thisPoint, nextPoint);
scalar d2 = magSqr(tri.centre() - seedPt);
if (d2 < minDistSqr)
{
minDistSqr = d2;
minFacei = facei;
minTetPtI = i-1;
}
fp = nextFp;
}
}
}
// Put particle in tet
return tetIndices
(
celli,
minFacei,
minTetPtI,
mesh_
);
}
void Foam::functionObjects::wallBoundedStreamLine::track()
{
// Determine the 'wall' patches
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// These are the faces that need to be followed
autoPtr<indirectPrimitivePatch> boundaryPatch(wallPatch());
PackedBoolList isWallPatch(mesh_.nFaces());
forAll(boundaryPatch().addressing(), i)
{
isWallPatch[boundaryPatch().addressing()[i]] = 1;
}
// Find nearest wall particle for the seedPoints
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IDLList<wallBoundedStreamLineParticle> initialParticles;
wallBoundedStreamLineParticleCloud particles
(
mesh_,
cloudName_,
initialParticles
);
{
// Get the seed points
// ~~~~~~~~~~~~~~~~~~~
const sampledSet& seedPoints = sampledSetPtr_();
forAll(seedPoints, i)
{
const point& seedPt = seedPoints[i];
label celli = seedPoints.cells()[i];
tetIndices ids(findNearestTet(isWallPatch, seedPt, celli));
if (ids.face() != -1 && isWallPatch[ids.face()])
{
//Pout<< "Seeding particle :" << nl
// << " seedPt:" << seedPt << nl
// << " face :" << ids.face() << nl
// << " at :" << mesh_.faceCentres()[ids.face()] << nl
// << " cell :" << mesh_.cellCentres()[ids.cell()] << nl
// << endl;
particles.addParticle
(
new wallBoundedStreamLineParticle
(
mesh_,
ids.faceTri(mesh_).centre(),
ids.cell(),
ids.face(), // tetFace
ids.tetPt(),
-1, // not on a mesh edge
-1, // not on a diagonal edge
lifeTime_ // lifetime
)
);
}
else
{
Pout<< type() << " : ignoring seed " << seedPt
<< " since not in wall cell." << endl;
}
}
}
label nSeeds = returnReduce(particles.size(), sumOp<label>());
Info<< type() << " : seeded " << nSeeds << " particles." << endl;
// Read or lookup fields
PtrList<volScalarField> vsFlds;
PtrList<interpolation<scalar>> vsInterp;
PtrList<volVectorField> vvFlds;
PtrList<interpolation<vector>> vvInterp;
label UIndex = -1;
label nScalar = 0;
label nVector = 0;
forAll(fields_, i)
{
if (mesh_.foundObject<volScalarField>(fields_[i]))
{
nScalar++;
}
else if (mesh_.foundObject<volVectorField>(fields_[i]))
{
nVector++;
}
else
{
FatalErrorInFunction
<< "Cannot find field " << fields_[i] << endl
<< "Valid scalar fields are:"
<< mesh_.names(volScalarField::typeName) << endl
<< "Valid vector fields are:"
<< mesh_.names(volVectorField::typeName)
<< exit(FatalError);
}
}
vsInterp.setSize(nScalar);
nScalar = 0;
vvInterp.setSize(nVector);
nVector = 0;
forAll(fields_, i)
{
if (mesh_.foundObject<volScalarField>(fields_[i]))
{
const volScalarField& f = mesh_.lookupObject<volScalarField>
(
fields_[i]
);
vsInterp.set
(
nScalar++,
interpolation<scalar>::New
(
interpolationScheme_,
f
)
);
}
else if (mesh_.foundObject<volVectorField>(fields_[i]))
{
const volVectorField& f = mesh_.lookupObject<volVectorField>
(
fields_[i]
);
if (f.name() == UName_)
{
UIndex = nVector;
}
vvInterp.set
(
nVector++,
interpolation<vector>::New
(
interpolationScheme_,
f
)
);
}
}
// Store the names
scalarNames_.setSize(vsInterp.size());
forAll(vsInterp, i)
{
scalarNames_[i] = vsInterp[i].psi().name();
}
vectorNames_.setSize(vvInterp.size());
forAll(vvInterp, i)
{
vectorNames_[i] = vvInterp[i].psi().name();
}
// Check that we know the index of U in the interpolators.
if (UIndex == -1)
{
FatalErrorInFunction
<< "Cannot find field to move particles with : " << UName_
<< endl
<< "This field has to be present in the sampled fields "
<< fields_
<< " and in the objectRegistry." << endl
<< exit(FatalError);
}
// Sampled data
// ~~~~~~~~~~~~
// Size to maximum expected sizes.
allTracks_.clear();
allTracks_.setCapacity(nSeeds);
allScalars_.setSize(vsInterp.size());
forAll(allScalars_, i)
{
allScalars_[i].clear();
allScalars_[i].setCapacity(nSeeds);
}
allVectors_.setSize(vvInterp.size());
forAll(allVectors_, i)
{
allVectors_[i].clear();
allVectors_[i].setCapacity(nSeeds);
}
// additional particle info
wallBoundedStreamLineParticle::trackingData td
(
particles,
vsInterp,
vvInterp,
UIndex, // index of U in vvInterp
trackForward_, // track in +u direction?
trackLength_, // fixed track length
isWallPatch, // which faces are to follow
allTracks_,
allScalars_,
allVectors_
);
// Set very large dt. Note: cannot use GREAT since 1/GREAT is SMALL
// which is a trigger value for the tracking...
const scalar trackTime = Foam::sqrt(GREAT);
// Track
particles.move(td, trackTime);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::wallBoundedStreamLine::wallBoundedStreamLine
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
fvMeshFunctionObject(name, runTime, dict),
dict_(dict)
{
read(dict_);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::wallBoundedStreamLine::~wallBoundedStreamLine()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::wallBoundedStreamLine::read(const dictionary& dict)
{
if (dict != dict_)
{
dict_ = dict;
}
Info<< type() << " " << name() << ":" << nl;
dict.lookup("fields") >> fields_;
dict.lookup("U") >> UName_;
if (findIndex(fields_, UName_) == -1)
{
FatalIOErrorInFunction
(
dict
) << "Velocity field for tracking " << UName_
<< " should be present in the list of fields " << fields_
<< exit(FatalIOError);
}
dict.lookup("trackForward") >> trackForward_;
dict.lookup("lifeTime") >> lifeTime_;
if (lifeTime_ < 1)
{
FatalErrorInFunction
<< "Illegal value " << lifeTime_ << " for lifeTime"
<< exit(FatalError);
}
trackLength_ = VGREAT;
if (dict.found("trackLength"))
{
dict.lookup("trackLength") >> trackLength_;
Info<< type() << " : fixed track length specified : "
<< trackLength_ << nl << endl;
}
interpolationScheme_ = dict.lookupOrDefault
(
"interpolationScheme",
interpolationCellPoint<scalar>::typeName
);
cloudName_ = dict.lookupOrDefault<word>
(
"cloudName",
"wallBoundedStreamLine"
);
sampledSetPtr_ = sampledSet::New
(
"seedSampleSet",
mesh_,
meshSearchMeshObject::New(mesh_),
dict.subDict("seedSampleSet")
);
sampledSetAxis_ = sampledSetPtr_->axis();
scalarFormatterPtr_ = writer<scalar>::New(dict.lookup("setFormat"));
vectorFormatterPtr_ = writer<vector>::New(dict.lookup("setFormat"));
// Make sure that the mesh is trackable
if (debug)
{
// 1. positive volume decomposition tets
faceSet faces(mesh_, "lowQualityTetFaces", mesh_.nFaces()/100+1);
if
(
polyMeshTetDecomposition::checkFaceTets
(
mesh_,
polyMeshTetDecomposition::minTetQuality,
true,
&faces
)
)
{
label nFaces = returnReduce(faces.size(), sumOp<label>());
WarningInFunction
<< "Found " << nFaces
<<" faces with low quality or negative volume "
<< "decomposition tets. Writing to faceSet " << faces.name()
<< endl;
}
// 2. all edges on a cell having two faces
EdgeMap<label> numFacesPerEdge;
forAll(mesh_.cells(), celli)
{
const cell& cFaces = mesh_.cells()[celli];
numFacesPerEdge.clear();
forAll(cFaces, cFacei)
{
label facei = cFaces[cFacei];
const face& f = mesh_.faces()[facei];
forAll(f, fp)
{
const edge e(f[fp], f.nextLabel(fp));
EdgeMap<label>::iterator eFnd =
numFacesPerEdge.find(e);
if (eFnd != numFacesPerEdge.end())
{
eFnd()++;
}
else
{
numFacesPerEdge.insert(e, 1);
}
}
}
forAllConstIter(EdgeMap<label>, numFacesPerEdge, iter)
{
if (iter() != 2)
{
FatalErrorInFunction
<< "problem cell:" << celli
<< abort(FatalError);
}
}
}
}
return true;
}
bool Foam::functionObjects::wallBoundedStreamLine::execute()
{
return true;
}
bool Foam::functionObjects::wallBoundedStreamLine::write()
{
const Time& runTime = obr_.time();
// Do all injection and tracking
track();
if (Pstream::parRun())
{
// Append slave tracks to master ones
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
globalIndex globalTrackIDs(allTracks_.size());
// Construct a distribution map to pull all to the master.
labelListList sendMap(Pstream::nProcs());
labelListList recvMap(Pstream::nProcs());
if (Pstream::master())
{
// Master: receive all. My own first, then consecutive
// processors.
label trackI = 0;
forAll(recvMap, proci)
{
labelList& fromProc = recvMap[proci];
fromProc.setSize(globalTrackIDs.localSize(proci));
forAll(fromProc, i)
{
fromProc[i] = trackI++;
}
}
}
labelList& toMaster = sendMap[0];
toMaster.setSize(globalTrackIDs.localSize());
forAll(toMaster, i)
{
toMaster[i] = i;
}
const mapDistribute distMap
(
globalTrackIDs.size(),
sendMap.xfer(),
recvMap.xfer()
);
// Distribute the track positions. Note: use scheduled comms
// to prevent buffering.
allTracks_.shrink();
mapDistributeBase::distribute
(
Pstream::commsTypes::scheduled,
distMap.schedule(),
distMap.constructSize(),
distMap.subMap(),
false,
distMap.constructMap(),
false,
allTracks_,
flipOp()
);
// Distribute the scalars
forAll(allScalars_, scalarI)
{
allScalars_[scalarI].shrink();
mapDistributeBase::distribute
(
Pstream::commsTypes::scheduled,
distMap.schedule(),
distMap.constructSize(),
distMap.subMap(),
false,
distMap.constructMap(),
false,
allScalars_[scalarI],
flipOp()
);
allScalars_[scalarI].setCapacity(allScalars_[scalarI].size());
}
// Distribute the vectors
forAll(allVectors_, vectorI)
{
allVectors_[vectorI].shrink();
mapDistributeBase::distribute
(
Pstream::commsTypes::scheduled,
distMap.schedule(),
distMap.constructSize(),
distMap.subMap(),
false,
distMap.constructMap(),
false,
allVectors_[vectorI],
flipOp()
);
allVectors_[vectorI].setCapacity(allVectors_[vectorI].size());
}
}
label n = 0;
forAll(allTracks_, trackI)
{
n += allTracks_[trackI].size();
}
Info<< " Tracks:" << allTracks_.size() << nl
<< " Total samples:" << n << endl;
// Massage into form suitable for writers
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if (Pstream::master() && allTracks_.size())
{
// Make output directory
fileName vtkPath
(
Pstream::parRun()
? runTime.path()/".."/"postProcessing"/"sets"/name()
: runTime.path()/"postProcessing"/"sets"/name()
);
if (mesh_.name() != fvMesh::defaultRegion)
{
vtkPath = vtkPath/mesh_.name();
}
vtkPath = vtkPath/mesh_.time().timeName();
mkDir(vtkPath);
// Convert track positions
PtrList<coordSet> tracks(allTracks_.size());
forAll(allTracks_, trackI)
{
tracks.set
(
trackI,
new coordSet
(
"track" + Foam::name(trackI),
sampledSetAxis_ //"xyz"
)
);
tracks[trackI].transfer(allTracks_[trackI]);
}
// Convert scalar values
if (allScalars_.size() > 0)
{
List<List<scalarField>> scalarValues(allScalars_.size());
forAll(allScalars_, scalarI)
{
DynamicList<scalarList>& allTrackVals =
allScalars_[scalarI];
scalarValues[scalarI].setSize(allTrackVals.size());
forAll(allTrackVals, trackI)
{
scalarList& trackVals = allTrackVals[trackI];
scalarValues[scalarI][trackI].transfer(trackVals);
}
}
fileName vtkFile
(
vtkPath
/ scalarFormatterPtr_().getFileName
(
tracks[0],
scalarNames_
)
);
Info<< " Writing data to " << vtkFile.path() << endl;
scalarFormatterPtr_().write
(
true, // writeTracks
tracks,
scalarNames_,
scalarValues,
OFstream(vtkFile)()
);
}
// Convert vector values
if (allVectors_.size() > 0)
{
List<List<vectorField>> vectorValues(allVectors_.size());
forAll(allVectors_, vectorI)
{
DynamicList<vectorList>& allTrackVals =
allVectors_[vectorI];
vectorValues[vectorI].setSize(allTrackVals.size());
forAll(allTrackVals, trackI)
{
vectorList& trackVals = allTrackVals[trackI];
vectorValues[vectorI][trackI].transfer(trackVals);
}
}
fileName vtkFile
(
vtkPath
/ vectorFormatterPtr_().getFileName
(
tracks[0],
vectorNames_
)
);
vectorFormatterPtr_().write
(
true, // writeTracks
tracks,
vectorNames_,
vectorValues,
OFstream(vtkFile)()
);
}
}
return true;
}
void Foam::functionObjects::wallBoundedStreamLine::updateMesh
(
const mapPolyMesh& mpm
)
{
if (&mpm.mesh() == &mesh_)
{
read(dict_);
}
}
void Foam::functionObjects::wallBoundedStreamLine::movePoints
(
const polyMesh& mesh
)
{
if (&mesh == &mesh_)
{
// Moving mesh affects the search tree
read(dict_);
}
}
// ************************************************************************* //

View File

@ -1,273 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
Class
Foam::functionObjects::wallBoundedStreamLine
Group
grpFieldFunctionObjects
Description
Generates streamline data by sampling a set of user-specified fields along a
particle track, transported by a user-specified velocity field, constrained
to a patch.
Example of function object specification:
\verbatim
wallBoundedStreamLine1
{
type wallBoundedStreamLine;
libs ("libfieldFunctionObjects.so");
writeControl writeTime;
setFormat vtk;
U UNear;
trackForward yes;
fields
(
UNear
p
);
lifeTime 10000;
trackLength 1e-3;
nSubCycle 5;
cloudName particleTracks;
seedSampleSet
{
type patchSeed;
patches (wall);
axis x;
maxPoints 20000;
}
}
\endverbatim
Usage
\table
Property | Description | Required | Default value
type | Type name: wallBoundedStreamLine| yes |
setFormat | Output data type | yes |
U | Tracking velocity field name | yes |
fields | Fields to sample | yes |
lifetime | Maximum number of particle tracking steps | yes |
trackLength | Tracking segment length | no |
nSubCycle | Number of tracking steps per cell | no|
cloudName | Cloud name to use | yes |
seedSampleSet| Seeding method (see below)| yes |
\endtable
Where \c seedSampleSet \c type is typically one of
\plaintable
uniform | uniform particle seeding
cloud | cloud of points
patchSeed | seeding via patch faces
triSurfaceMeshPointSet | points according to a tri-surface mesh
\endplaintable
Note
When specifying the track resolution, the \c trackLength OR \c nSubCycle
option should be used
See also
Foam::fvMeshFunctionObject
Foam::functionObjects::timeControl
Foam::sampledSet
Foam::streamLine
SourceFiles
wallBoundedStreamLine.C
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_wallBoundedStreamLine_H
#define functionObjects_wallBoundedStreamLine_H
#include "fvMeshFunctionObject.H"
#include "volFieldsFwd.H"
#include "DynamicList.H"
#include "scalarList.H"
#include "vectorList.H"
#include "writer.H"
#include "indirectPrimitivePatch.H"
#include "tetIndices.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of classes
class meshSearch;
class sampledSet;
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class wallBoundedStreamLine Declaration
\*---------------------------------------------------------------------------*/
class wallBoundedStreamLine
:
public fvMeshFunctionObject
{
// Private data
//- Input dictionary
dictionary dict_;
//- List of fields to sample
wordList fields_;
//- Field to transport particle with
word UName_;
//- Interpolation scheme to use
word interpolationScheme_;
//- Whether to use +u or -u
bool trackForward_;
//- Maximum lifetime (= number of cells) of particle
label lifeTime_;
//- Track length
scalar trackLength_;
//- Optional specified name of particles
word cloudName_;
//- Type of seed
word seedSet_;
//- Names of scalar fields
wordList scalarNames_;
//- Names of vector fields
wordList vectorNames_;
// Demand driven
//- Mesh searching enigne
autoPtr<meshSearch> meshSearchPtr_;
//- Seed set engine
autoPtr<sampledSet> sampledSetPtr_;
//- Axis of the sampled points to output
word sampledSetAxis_;
//- File output writer
autoPtr<writer<scalar>> scalarFormatterPtr_;
autoPtr<writer<vector>> vectorFormatterPtr_;
// Generated data
//- All tracks. Per particle the points it passed through
DynamicList<List<point>> allTracks_;
//- Per scalarField, per particle, the sampled value.
List<DynamicList<scalarList>> allScalars_;
//- Per scalarField, per particle, the sampled value.
List<DynamicList<vectorList>> allVectors_;
//- Construct patch out of all wall patch faces
autoPtr<indirectPrimitivePatch> wallPatch() const;
//- Find wall tet on cell
tetIndices findNearestTet
(
const PackedBoolList& isWallPatch,
const point& seedPt,
const label celli
) const;
//- Do all seeding and tracking
void track();
//- Disallow default bitwise copy construct
wallBoundedStreamLine(const wallBoundedStreamLine&);
//- Disallow default bitwise assignment
void operator=(const wallBoundedStreamLine&);
public:
//- Runtime type information
TypeName("wallBoundedStreamLine");
// Constructors
//- Construct from Time and dictionary
wallBoundedStreamLine
(
const word& name,
const Time& runTime,
const dictionary& dict
);
//- Destructor
virtual ~wallBoundedStreamLine();
// Member Functions
//- Read the field average data
virtual bool read(const dictionary&);
//- Do nothing
virtual bool execute();
//- Calculate and write the wall-bounded streamlines
virtual bool write();
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh&);
//- Update for mesh point-motion
virtual void movePoints(const polyMesh&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,435 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "wallBoundedStreamLineParticle.H"
#include "vectorFieldIOField.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::vector Foam::wallBoundedStreamLineParticle::interpolateFields
(
const trackingData& td,
const point& position,
const label celli,
const label facei
)
{
if (celli == -1)
{
FatalErrorInFunction
<< "Cell:" << celli << abort(FatalError);
}
const tetIndices ti = currentTetIndices();
const vector U = td.vvInterp_[td.UIndex_].interpolate
(
position,
ti, //celli,
facei
);
// Check if at different position
if
(
!sampledPositions_.size()
|| magSqr(sampledPositions_.last()-position) > Foam::sqr(SMALL)
)
{
// Store the location
sampledPositions_.append(position);
// Store the scalar fields
sampledScalars_.setSize(td.vsInterp_.size());
forAll(td.vsInterp_, scalarI)
{
sampledScalars_[scalarI].append
(
td.vsInterp_[scalarI].interpolate
(
position,
ti, //celli,
facei
)
);
}
// Store the vector fields
sampledVectors_.setSize(td.vvInterp_.size());
forAll(td.vvInterp_, vectorI)
{
vector positionU;
if (vectorI == td.UIndex_)
{
positionU = U;
}
else
{
positionU = td.vvInterp_[vectorI].interpolate
(
position,
ti, //celli,
facei
);
}
sampledVectors_[vectorI].append(positionU);
}
}
return U;
}
Foam::vector Foam::wallBoundedStreamLineParticle::sample
(
trackingData& td
)
{
vector U = interpolateFields(td, position(), cell(), tetFace());
if (!td.trackForward_)
{
U = -U;
}
scalar magU = mag(U);
if (magU < SMALL)
{
// Stagnant particle. Might as well stop
lifeTime_ = 0;
}
U /= magU;
return U;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle
(
const polyMesh& mesh,
const vector& position,
const label celli,
const label tetFacei,
const label tetPtI,
const label meshEdgeStart,
const label diagEdge,
const label lifeTime
)
:
wallBoundedParticle
(
mesh,
position,
celli,
tetFacei,
tetPtI,
meshEdgeStart,
diagEdge
),
lifeTime_(lifeTime)
{}
Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle
(
const polyMesh& mesh,
Istream& is,
bool readFields
)
:
wallBoundedParticle(mesh, is, readFields)
{
if (readFields)
{
List<scalarList> sampledScalars;
List<vectorList> sampledVectors;
is >> lifeTime_
>> sampledPositions_ >> sampledScalars >> sampledVectors;
sampledScalars_.setSize(sampledScalars.size());
forAll(sampledScalars, i)
{
sampledScalars_[i].transfer(sampledScalars[i]);
}
sampledVectors_.setSize(sampledVectors.size());
forAll(sampledVectors, i)
{
sampledVectors_[i].transfer(sampledVectors[i]);
}
}
// Check state of Istream
is.check
(
"wallBoundedStreamLineParticle::wallBoundedStreamLineParticle"
"(const Cloud<wallBoundedStreamLineParticle>&, Istream&, bool)"
);
}
Foam::wallBoundedStreamLineParticle::wallBoundedStreamLineParticle
(
const wallBoundedStreamLineParticle& p
)
:
wallBoundedParticle(p),
lifeTime_(p.lifeTime_),
sampledPositions_(p.sampledPositions_),
sampledScalars_(p.sampledScalars_),
sampledVectors_(p.sampledVectors_)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::wallBoundedStreamLineParticle::move
(
trackingData& td,
const scalar trackTime
)
{
wallBoundedStreamLineParticle& p = static_cast
<
wallBoundedStreamLineParticle&
>(*this);
// Check position is inside tet
//checkInside();
td.switchProcessor = false;
td.keepParticle = true;
scalar tEnd = (1.0 - stepFraction())*trackTime;
scalar maxDt = mesh_.bounds().mag();
while
(
td.keepParticle
&& !td.switchProcessor
&& lifeTime_ > 0
)
{
// set the lagrangian time-step
scalar dt = maxDt;
--lifeTime_;
// Get sampled velocity and fields. Store if position changed.
vector U = sample(td);
// !user parameter!
if (dt < SMALL)
{
// Force removal
lifeTime_ = 0;
break;
}
if (td.trackLength_ < GREAT)
{
dt = td.trackLength_;
}
scalar fraction = trackToEdge(td, position() + dt*U);
dt *= fraction;
tEnd -= dt;
stepFraction() = 1.0 - tEnd/trackTime;
if (tEnd <= ROOTVSMALL)
{
// Force removal
lifeTime_ = 0;
}
if
(
!td.keepParticle
|| td.switchProcessor
|| lifeTime_ == 0
)
{
break;
}
}
if (!td.keepParticle || lifeTime_ == 0)
{
if (lifeTime_ == 0)
{
if (debug)
{
Pout<< "wallBoundedStreamLineParticle :"
<< " Removing stagnant particle:"
<< p.position()
<< " sampled positions:" << sampledPositions_.size()
<< endl;
}
td.keepParticle = false;
}
else
{
// Normal exit. Store last position and fields
sample(td);
if (debug)
{
Pout<< "wallBoundedStreamLineParticle : Removing particle:"
<< p.position()
<< " sampled positions:" << sampledPositions_.size()
<< endl;
}
}
// Transfer particle data into trackingData.
{
//td.allPositions_.append(sampledPositions_);
td.allPositions_.append(vectorList());
vectorList& top = td.allPositions_.last();
top.transfer(sampledPositions_);
}
forAll(sampledScalars_, i)
{
//td.allScalars_[i].append(sampledScalars_[i]);
td.allScalars_[i].append(scalarList());
scalarList& top = td.allScalars_[i].last();
top.transfer(sampledScalars_[i]);
}
forAll(sampledVectors_, i)
{
//td.allVectors_[i].append(sampledVectors_[i]);
td.allVectors_[i].append(vectorList());
vectorList& top = td.allVectors_[i].last();
top.transfer(sampledVectors_[i]);
}
}
return td.keepParticle;
}
void Foam::wallBoundedStreamLineParticle::readFields
(
Cloud<wallBoundedStreamLineParticle>& c
)
{
if (!c.size())
{
return;
}
wallBoundedParticle::readFields(c);
IOField<label> lifeTime
(
c.fieldIOobject("lifeTime", IOobject::MUST_READ)
);
c.checkFieldIOobject(c, lifeTime);
vectorFieldIOField sampledPositions
(
c.fieldIOobject("sampledPositions", IOobject::MUST_READ)
);
c.checkFieldIOobject(c, sampledPositions);
label i = 0;
forAllIter(Cloud<wallBoundedStreamLineParticle>, c, iter)
{
iter().lifeTime_ = lifeTime[i];
iter().sampledPositions_.transfer(sampledPositions[i]);
i++;
}
}
void Foam::wallBoundedStreamLineParticle::writeFields
(
const Cloud<wallBoundedStreamLineParticle>& c
)
{
wallBoundedParticle::writeFields(c);
label np = c.size();
IOField<label> lifeTime
(
c.fieldIOobject("lifeTime", IOobject::NO_READ),
np
);
vectorFieldIOField sampledPositions
(
c.fieldIOobject("sampledPositions", IOobject::NO_READ),
np
);
label i = 0;
forAllConstIter(Cloud<wallBoundedStreamLineParticle>, c, iter)
{
lifeTime[i] = iter().lifeTime_;
sampledPositions[i] = iter().sampledPositions_;
i++;
}
lifeTime.write();
sampledPositions.write();
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const wallBoundedStreamLineParticle& p
)
{
os << static_cast<const wallBoundedParticle&>(p)
<< token::SPACE << p.lifeTime_
<< token::SPACE << p.sampledPositions_
<< token::SPACE << p.sampledScalars_
<< token::SPACE << p.sampledVectors_;
// Check state of Ostream
os.check
(
"Ostream& operator<<(Ostream&, const wallBoundedStreamLineParticle&)"
);
return os;
}
// ************************************************************************* //

View File

@ -1,260 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
Class
Foam::wallBoundedStreamLineParticle
Description
Particle class that samples fields as it passes through. Used in streamline
calculation.
SourceFiles
wallBoundedStreamLineParticle.C
\*---------------------------------------------------------------------------*/
#ifndef wallBoundedStreamLineParticle_H
#define wallBoundedStreamLineParticle_H
#include "wallBoundedParticle.H"
#include "autoPtr.H"
#include "interpolation.H"
#include "vectorList.H"
#include "InfoProxy.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class wallBoundedStreamLineParticleCloud;
// Forward declaration of friend functions and operators
class wallBoundedStreamLineParticle;
Ostream& operator<<(Ostream&, const wallBoundedStreamLineParticle&);
/*---------------------------------------------------------------------------*\
Class wallBoundedStreamLineParticle Declaration
\*---------------------------------------------------------------------------*/
class wallBoundedStreamLineParticle
:
public wallBoundedParticle
{
public:
//- Class used to pass tracking data to the trackToEdge function
class trackingData
:
public wallBoundedParticle::TrackingData
<
Cloud<wallBoundedStreamLineParticle>
>
{
public:
const PtrList<interpolation<scalar>>& vsInterp_;
const PtrList<interpolation<vector>>& vvInterp_;
const label UIndex_;
const bool trackForward_;
const scalar trackLength_;
DynamicList<vectorList>& allPositions_;
List<DynamicList<scalarList>>& allScalars_;
List<DynamicList<vectorList>>& allVectors_;
// Constructors
trackingData
(
Cloud<wallBoundedStreamLineParticle>& cloud,
const PtrList<interpolation<scalar>>& vsInterp,
const PtrList<interpolation<vector>>& vvInterp,
const label UIndex,
const bool trackForward,
const scalar trackLength,
const PackedBoolList& isWallPatch,
DynamicList<List<point>>& allPositions,
List<DynamicList<scalarList>>& allScalars,
List<DynamicList<vectorList>>& allVectors
)
:
wallBoundedParticle::TrackingData
<
Cloud<wallBoundedStreamLineParticle>
>
(
cloud,
isWallPatch
),
vsInterp_(vsInterp),
vvInterp_(vvInterp),
UIndex_(UIndex),
trackForward_(trackForward),
trackLength_(trackLength),
allPositions_(allPositions),
allScalars_(allScalars),
allVectors_(allVectors)
{}
};
private:
// Private data
//- Lifetime of particle. Particle dies when reaches 0.
label lifeTime_;
//- Sampled positions
DynamicList<point> sampledPositions_;
//- Sampled scalars
List<DynamicList<scalar>> sampledScalars_;
//- Sampled vectors
List<DynamicList<vector>> sampledVectors_;
// Private Member Functions
vector interpolateFields
(
const trackingData& td,
const point& position,
const label celli,
const label facei
);
vector sample(trackingData& td);
public:
// Constructors
//- Construct from components
wallBoundedStreamLineParticle
(
const polyMesh& c,
const vector& position,
const label celli,
const label tetFacei,
const label tetPtI,
const label meshEdgeStart,
const label diagEdge,
const label lifeTime
);
//- Construct from Istream
wallBoundedStreamLineParticle
(
const polyMesh& c,
Istream& is,
bool readFields = true
);
//- Construct copy
wallBoundedStreamLineParticle(const wallBoundedStreamLineParticle& p);
//- Construct and return a clone
autoPtr<particle> clone() const
{
return autoPtr<particle>(new wallBoundedStreamLineParticle(*this));
}
//- Factory class to read-construct particles used for
// parallel transfer
class iNew
{
const polyMesh& mesh_;
public:
iNew(const polyMesh& mesh)
:
mesh_(mesh)
{}
autoPtr<wallBoundedStreamLineParticle> operator()
(
Istream& is
) const
{
return autoPtr<wallBoundedStreamLineParticle>
(
new wallBoundedStreamLineParticle(mesh_, is, true)
);
}
};
// Member Functions
// Tracking
//- Track all particles to their end point
bool move(trackingData&, const scalar trackTime);
// I-O
//- Read
static void readFields(Cloud<wallBoundedStreamLineParticle>&);
//- Write
static void writeFields
(
const Cloud<wallBoundedStreamLineParticle>&
);
// Ostream Operator
friend Ostream& operator<<
(
Ostream&,
const wallBoundedStreamLineParticle&
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,64 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "wallBoundedStreamLineParticleCloud.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTemplateTypeNameAndDebug(Cloud<wallBoundedStreamLineParticle>, 0);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::wallBoundedStreamLineParticleCloud::wallBoundedStreamLineParticleCloud
(
const polyMesh& mesh,
const word& cloudName,
bool readFields
)
:
Cloud<wallBoundedStreamLineParticle>(mesh, cloudName, false)
{
if (readFields)
{
wallBoundedStreamLineParticle::readFields(*this);
}
}
Foam::wallBoundedStreamLineParticleCloud::wallBoundedStreamLineParticleCloud
(
const polyMesh& mesh,
const word& cloudName,
const IDLList<wallBoundedStreamLineParticle>& particles
)
:
Cloud<wallBoundedStreamLineParticle>(mesh, cloudName, particles)
{}
// ************************************************************************* //

View File

@ -1,99 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ 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 <http://www.gnu.org/licenses/>.
Class
Foam::wallBoundedStreamLineParticleCloud
Description
A Cloud of streamLine particles
SourceFiles
streamLineCloud.C
\*---------------------------------------------------------------------------*/
#ifndef streamLineCloud_H
#define streamLineCloud_H
#include "Cloud.H"
#include "wallBoundedStreamLineParticle.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class streamLineCloud Declaration
\*---------------------------------------------------------------------------*/
class wallBoundedStreamLineParticleCloud
:
public Cloud<wallBoundedStreamLineParticle>
{
// Private Member Functions
//- Disallow default bitwise copy construct
wallBoundedStreamLineParticleCloud
(
const wallBoundedStreamLineParticleCloud&
);
//- Disallow default bitwise assignment
void operator=(const wallBoundedStreamLineParticleCloud&);
public:
//- Type of parcel the cloud was instantiated for
typedef wallBoundedStreamLineParticle parcelType;
// Constructors
//- Construct given mesh
wallBoundedStreamLineParticleCloud
(
const polyMesh&,
const word& cloudName = "defaultCloud",
bool readFields = true
);
//- Construct from mesh, cloud name, and a list of particles
wallBoundedStreamLineParticleCloud
(
const polyMesh& mesh,
const word& cloudName,
const IDLList<wallBoundedStreamLineParticle>& particles
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //