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:
@ -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
|
||||
|
||||
@ -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_)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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)
|
||||
{}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -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
|
||||
|
||||
// ************************************************************************* //
|
||||
Reference in New Issue
Block a user