Files
OpenFOAM-12/src/functionObjects/field/streamlines/streamlinesParticle.C
2023-07-11 11:02:47 +01:00

610 lines
16 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2023 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 "streamlinesParticle.H"
#include "streamlinesCloud.H"
#include "vectorFieldIOField.H"
#include "scalarFieldIOField.H"
#include "transformerIOList.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::vector Foam::streamlinesParticle::interpolateFields
(
const trackingData& td,
const point& position,
const label celli,
const label facei
)
{
if (celli == -1)
{
FatalErrorInFunction
<< "Cell:" << celli << abort(FatalError);
}
bool interpolatedU = false;
vector U = vector::uniform(NaN);
forAll(td.scalarInterp_, fieldi)
{
#define InterpolateType(Type, nullArg) \
if (td.Type##Interp_.set(fieldi)) \
{ \
const Type s = \
td.Type##Interp_[fieldi].interpolate \
( \
position, \
celli, \
facei \
); \
\
sampled##Type##s_.setSize(td.Type##Interp_.size()); \
sampled##Type##s_[fieldi].append \
( \
td.trackOutside_ ? transform_.invTransform(s) : s \
); \
}
FOR_ALL_FIELD_TYPES(InterpolateType);
#undef InterpolateType
if
(
td.vectorInterp_.set(fieldi)
&& &td.vectorInterp_[fieldi] == &td.UInterp_
)
{
interpolatedU = true;
U = sampledvectors_[fieldi].last();
}
}
// Interpolate the velocity if it has not already been done
if (!interpolatedU)
{
U = td.UInterp_.interpolate(position, celli, facei);
}
return U;
}
void Foam::streamlinesParticle::endTrack(trackingData& td)
{
const label n = sampledPositions_.size();
const label trackPartIndex =
td.trackForward_ ? trackPartIndex_ : -1 - trackPartIndex_;
if (!td.trackForward_) reverse(sampledPositions_);
td.allPositions_.append(sampledPositions_);
sampledPositions_.clear();
td.allTracks_.append(List<label>(n, trackIndex_));
td.allTrackParts_.append(List<label>(n, trackPartIndex));
trackPartIndex_ ++;
if (!td.trackForward_) reverse(sampledAges_);
td.allAges_.append(sampledAges_);
sampledAges_.clear();
forAll(td.scalarInterp_, fieldi)
{
#define EndTrackType(Type, nullArg) \
if (td.Type##Interp_.set(fieldi)) \
{ \
if (!td.trackForward_) reverse(sampled##Type##s_[fieldi]); \
td.all##Type##s_[fieldi].append(sampled##Type##s_[fieldi]); \
sampled##Type##s_[fieldi].clear(); \
}
FOR_ALL_FIELD_TYPES(EndTrackType);
#undef EndTrackType
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::streamlinesParticle::streamlinesParticle
(
const polyMesh& mesh,
const vector& position,
const label celli,
const label lifeTime,
const label trackIndex
)
:
particle(mesh, position, celli),
lifeTime_(lifeTime),
trackIndex_(trackIndex),
trackPartIndex_(0),
age_(0),
transform_(transformer::I)
{}
Foam::streamlinesParticle::streamlinesParticle(Istream& is, bool readFields)
:
particle(is, readFields)
{
if (readFields)
{
is >> lifeTime_ >> trackIndex_ >> trackPartIndex_ >> age_
>> transform_ >> sampledPositions_ >> sampledAges_;
#define ReadSampledTypes(Type, nullArg) \
List<List<Type>> sampled##Type##s; \
is >> sampled##Type##s; \
sampled##Type##s_.setSize(sampled##Type##s.size()); \
forAll(sampled##Type##s, i) \
{ \
sampled##Type##s_[i].transfer(sampled##Type##s[i]); \
}
FOR_ALL_FIELD_TYPES(ReadSampledTypes);
#undef ReadSampledTypes
}
// Check state of Istream
is.check
(
"streamlinesParticle::streamlinesParticle"
"(const Cloud<streamlinesParticle>&, Istream&, bool)"
);
}
Foam::streamlinesParticle::streamlinesParticle
(
const streamlinesParticle& p
)
:
particle(p),
lifeTime_(p.lifeTime_),
trackIndex_(p.trackIndex_),
trackPartIndex_(p.trackPartIndex_),
age_(p.age_),
transform_(p.transform_),
sampledPositions_(p.sampledPositions_),
sampledAges_(p.sampledAges_)
#define SampledTypesInit(Type, nullArg) \
, sampled##Type##s_(p.sampled##Type##s_)
FOR_ALL_FIELD_TYPES(SampledTypesInit)
#undef SampledTypesInit
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::streamlinesParticle::move
(
streamlinesCloud& cloud,
trackingData& td
)
{
td.keepParticle = true;
td.sendToProc = -1;
const scalar maxDt = td.mesh.bounds().mag();
while (td.keepParticle && td.sendToProc == -1 && lifeTime_ > 0)
{
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 < max(1, td.nSubCycle_); subIter++)
{
--lifeTime_;
// Store current position and sampled velocity.
sampledPositions_.append
(
td.trackOutside_
? transform_.invTransformPosition(position(td.mesh))
: position(td.mesh)
);
sampledAges_.append(age_);
vector U = interpolateFields(td, position(td.mesh), cell(), face());
if (!td.trackForward_)
{
U = -U;
}
scalar magU = mag(U);
if (magU < small)
{
// Stagnant particle. Might as well stop
lifeTime_ = 0;
break;
}
U /= magU;
if (td.trackLength_ < great)
{
// No sub-cycling. Track a set length on each step.
dt = td.trackLength_;
}
else if (subIter == 0)
{
// Sub-cycling. Cross the cell in nSubCycle steps.
particle copy(*this);
copy.trackToFace(td.mesh, maxDt*U, 1);
dt *= (copy.stepFraction() - stepFraction())/td.nSubCycle_;
}
else if (subIter == td.nSubCycle_ - 1)
{
// Sub-cycling. Track the whole cell on the last step.
dt = maxDt;
}
age_ +=
(td.trackForward_ ? +1 : -1)
*dt/magU
*(1 - trackToAndHitFace(dt*U, 0, cloud, td));
if (!td.keepParticle || td.sendToProc != -1 || lifeTime_ == 0)
{
break;
}
}
}
if (!td.keepParticle || lifeTime_ == 0)
{
if (lifeTime_ == 0)
{
// Failure exit. Particle stagnated or its life ran out.
if (debug)
{
Pout<< "streamlinesParticle: Removing stagnant particle:"
<< position(td.mesh) << " sampled positions:"
<< sampledPositions_.size() << endl;
}
td.keepParticle = false;
}
else
{
// Normal exit. Store last position and fields
sampledPositions_.append
(
td.trackOutside_
? transform_.invTransformPosition(position(td.mesh))
: position(td.mesh)
);
sampledAges_.append(age_);
interpolateFields(td, position(td.mesh), cell(), face());
if (debug)
{
Pout<< "streamlinesParticle: Removing particle:"
<< position(td.mesh) << " sampled positions:"
<< sampledPositions_.size() << endl;
}
}
// End this track
endTrack(td);
}
return td.keepParticle;
}
void Foam::streamlinesParticle::hitWedgePatch
(
streamlinesCloud&,
trackingData& td
)
{
// Remove particle
td.keepParticle = false;
}
void Foam::streamlinesParticle::hitSymmetryPlanePatch
(
streamlinesCloud&,
trackingData& td
)
{
// Remove particle
td.keepParticle = false;
}
void Foam::streamlinesParticle::hitSymmetryPatch
(
streamlinesCloud&,
trackingData& td
)
{
// Remove particle
td.keepParticle = false;
}
void Foam::streamlinesParticle::hitCyclicPatch
(
streamlinesCloud& cloud,
trackingData& td
)
{
const cyclicPolyPatch& cpp =
static_cast<const cyclicPolyPatch&>
(
td.mesh.boundaryMesh()[patch(td.mesh)]
);
// End this track
if (!td.trackOutside_ && cpp.transform().transformsPosition())
{
endTrack(td);
}
// Move across the cyclic
particle::hitCyclicPatch(cloud, td);
}
bool Foam::streamlinesParticle::hitNonConformalCyclicPatch
(
const vector& displacement,
const scalar fraction,
const label patchi,
streamlinesCloud& cloud,
trackingData& td
)
{
// Move across the cyclic
const bool result =
particle::hitNonConformalCyclicPatch
(
displacement,
fraction,
patchi,
cloud,
td
);
// End this track
if (result) endTrack(td);
return result;
}
void Foam::streamlinesParticle::hitProcessorPatch
(
streamlinesCloud& cloud,
trackingData& td
)
{
const processorPolyPatch& ppp =
static_cast<const processorPolyPatch&>
(
td.mesh.boundaryMesh()[patch(td.mesh)]
);
// End this track
if (!td.trackOutside_ && ppp.transform().transformsPosition())
{
endTrack(td);
}
particle::hitProcessorPatch(cloud, td);
}
void Foam::streamlinesParticle::hitWallPatch
(
streamlinesCloud&,
trackingData& td
)
{
// Remove particle
td.keepParticle = false;
}
void Foam::streamlinesParticle::transformProperties
(
const transformer& transform
)
{
transform_ = transform & transform_;
}
void Foam::streamlinesParticle::readFields(Cloud<streamlinesParticle>& c)
{
bool valid = c.size();
particle::readFields(c);
IOField<label> lifeTime
(
c.fieldIOobject("lifeTime", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, lifeTime);
IOField<label> trackIndex
(
c.fieldIOobject("trackIndex", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, trackIndex);
IOField<label> trackPartIndex
(
c.fieldIOobject("trackPartIndex", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, trackPartIndex);
IOField<scalar> age
(
c.fieldIOobject("age", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, age);
transformerIOList transform
(
c.fieldIOobject("transform", IOobject::MUST_READ),
valid
);
//c.checkFieldIOobject(c, transform);
vectorFieldIOField sampledPositions
(
c.fieldIOobject("sampledPositions", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, sampledPositions);
scalarFieldIOField sampledAges
(
c.fieldIOobject("sampledAges", IOobject::MUST_READ),
valid
);
c.checkFieldIOobject(c, sampledAges);
label i = 0;
forAllIter(Cloud<streamlinesParticle>, c, iter)
{
iter().lifeTime_ = lifeTime[i];
iter().trackIndex_ = trackIndex[i];
iter().trackPartIndex_ = trackPartIndex[i];
iter().age_ = age[i];
iter().transform_ = transform[i];
iter().sampledPositions_.transfer(sampledPositions[i]);
iter().sampledAges_.transfer(sampledAges[i]);
i++;
}
}
void Foam::streamlinesParticle::writeFields(const Cloud<streamlinesParticle>& c)
{
particle::writeFields(c);
label np = c.size();
IOField<label> lifeTime
(
c.fieldIOobject("lifeTime", IOobject::NO_READ),
np
);
IOList<label> trackIndex
(
c.fieldIOobject("trackIndex", IOobject::NO_READ),
np
);
IOList<label> trackPartIndex
(
c.fieldIOobject("trackPartIndex", IOobject::NO_READ),
np
);
IOField<scalar> age
(
c.fieldIOobject("age", IOobject::NO_READ),
np
);
transformerIOList transform
(
c.fieldIOobject("transform", IOobject::NO_READ),
np
);
vectorFieldIOField sampledPositions
(
c.fieldIOobject("sampledPositions", IOobject::NO_READ),
np
);
scalarFieldIOField sampledAges
(
c.fieldIOobject("sampledAges", IOobject::NO_READ),
np
);
label i = 0;
forAllConstIter(Cloud<streamlinesParticle>, c, iter)
{
lifeTime[i] = iter().lifeTime_;
trackIndex[i] = iter().trackIndex_;
trackPartIndex[i] = iter().trackPartIndex_;
age[i] = iter().age_;
transform[i] = iter().transform_;
sampledPositions[i] = iter().sampledPositions_;
sampledAges[i] = iter().sampledAges_;
i++;
}
lifeTime.write(np > 0);
trackIndex.write(np > 0);
trackPartIndex.write(np > 0);
age.write(np > 0);
transform.write(np > 0);
sampledPositions.write(np > 0);
sampledAges.write(np > 0);
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<(Ostream& os, const streamlinesParticle& p)
{
os << static_cast<const particle&>(p)
<< token::SPACE << p.lifeTime_
<< token::SPACE << p.trackIndex_
<< token::SPACE << p.trackPartIndex_
<< token::SPACE << p.age_
<< token::SPACE << p.transform_
<< token::SPACE << p.sampledPositions_
<< token::SPACE << p.sampledAges_
#define WriteSampledTypes(Type, nullArg) \
<< token::SPACE << p.sampled##Type##s_
FOR_ALL_FIELD_TYPES(WriteSampledTypes);
#undef WriteSampledTypes
// Check state of Ostream
os.check("Ostream& operator<<(Ostream&, const streamlinesParticle&)");
return os;
}
// ************************************************************************* //