Newer
Older
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
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 "ThermoSurfaceFilm.H"
#include "surfaceFilmRegionModel.H"
#include "liquidFilmBase.H"
#include "addToRunTimeSelectionTable.H"
#include "Pstream.H"
using namespace Foam::constant::mathematical;
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
template<class CloudType>
Foam::wordList Foam::ThermoSurfaceFilm<CloudType>::interactionTypeNames_
{
"absorb", "bounce", "splashBai"
};
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
template<class CloudType>
typename Foam::ThermoSurfaceFilm<CloudType>::interactionType
Foam::ThermoSurfaceFilm<CloudType>::interactionTypeEnum(const word& it) const
{
forAll(interactionTypeNames_, i)
{
if (interactionTypeNames_[i] == it)
{
return interactionType(i);
}
}
FatalErrorInFunction
<< "Unknown interaction type " << it
<< ". Valid interaction types include: " << interactionTypeNames_
<< abort(FatalError);
return interactionType(0);
}
template<class CloudType>
Foam::word Foam::ThermoSurfaceFilm<CloudType>::interactionTypeStr
(
const interactionType& it
) const
{
if (it >= interactionTypeNames_.size())
{
FatalErrorInFunction
<< "Unknown interaction type enumeration" << abort(FatalError);
}
return interactionTypeNames_[it];
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class CloudType>
Foam::vector Foam::ThermoSurfaceFilm<CloudType>::tangentVector
(
const vector& v
) const
{
scalar magTangent = 0.0;
while (magTangent < SMALL)
{
vector vTest = rndGen_.sample01<vector>();
tangent = vTest - (vTest & v)*v;
magTangent = mag(tangent);
}
return tangent/magTangent;
}
template<class CloudType>
Foam::vector Foam::ThermoSurfaceFilm<CloudType>::splashDirection
(
const vector& tanVec1,
const vector& tanVec2,
const vector& nf
) const
{
Henry Weller
committed
// Azimuthal angle [rad]
const scalar phiSi = twoPi*rndGen_.sample01<scalar>();
Henry Weller
committed
// Ejection angle [rad]
const scalar thetaSi = degToRad(rndGen_.sample01<scalar>()*(50 - 5) + 5);
Henry Weller
committed
// Direction vector of new parcel
const scalar alpha = sin(thetaSi);
const scalar dcorr = cos(thetaSi);
const vector normal = alpha*(tanVec1*cos(phiSi) + tanVec2*sin(phiSi));
vector dirVec = dcorr*nf;
dirVec += normal;
return dirVec/mag(dirVec);
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::absorbInteraction
(
const parcelType& p,
const polyPatch& pp,
const scalar mass,
bool& keepParticle
)
{
if (debug)
{
Info<< "Parcel " << p.origId() << " absorbInteraction" << endl;
}
// Patch face normal
const vector& nf = pp.faceNormals()[facei];
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
// Relative parcel velocity
const vector Urel = p.U() - Up;
// Parcel normal velocity
const vector Un = nf*(Urel & nf);
// Parcel tangential velocity
const vector Ut = Urel - Un;
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
if (filmModel_)
{
filmModel_->addSources
(
pp.index(),
facei,
mass, // mass
mass*Ut, // tangential momentum
mass*mag(Un), // impingement pressure
mass*p.hs() // energy
);
this->nParcelsTransferred()++;
keepParticle = false;
}
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::absorbInteraction
(
areaFilm& filmModel,
const parcelType& p,
const polyPatch& pp,
const label facei,
const scalar mass,
bool& keepParticle
)
{
if (debug)
{
Info<< "Parcel " << p.origId() << " absorbInteraction" << endl;
}
// Patch face normal
const vector& nf = pp.faceNormals()[facei];
// Patch velocity
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
// Relative parcel velocity
const vector Urel = p.U() - Up;
// Parcel normal velocity
const vector Un = nf*(Urel & nf);
// Parcel tangential velocity
const vector Ut = Urel - Un;
DebugVar(mass)
filmModel.addSources
(
pp.index(),
mass, // mass
mass*Ut, // tangential momentum
mass*mag(Un), // impingement pressure
mass*p.hs() // energy
);
this->nParcelsTransferred()++;
keepParticle = false;
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::bounceInteraction
(
parcelType& p,
const polyPatch& pp,
bool& keepParticle
) const
{
if (debug)
{
Info<< "Parcel " << p.origId() << " bounceInteraction" << endl;
}
// Patch face normal
const vector& nf = pp.faceNormals()[facei];
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
// Relative parcel velocity
const vector Urel = p.U() - Up;
// Flip parcel normal velocity component
p.U() -= 2.0*nf*(Urel & nf);
keepParticle = true;
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::drySplashInteraction
(
//regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
const parcelType& p,
const polyPatch& pp,
bool& keepParticle
)
{
if (debug)
{
Info<< "Parcel " << p.origId() << " drySplashInteraction" << endl;
}
const liquidProperties& liq = thermo_.liquids().properties()[0];
// Patch face velocity and normal
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
const vector& nf = pp.faceNormals()[facei];
Henry Weller
committed
// Local pressure
const scalar pc = thermo_.thermo().p()[p.cell()];
// Retrieve parcel properties
const scalar m = p.mass()*p.nParticle();
const scalar rho = p.rho();
const scalar d = p.d();
const scalar sigma = liq.sigma(pc, p.T());
const scalar mu = liq.mu(pc, p.T());
const vector Urel = p.U() - Up;
const vector Un = nf*(Urel & nf);
// Laplace number
const scalar La = rho*sigma*d/sqr(mu);
// Weber number
const scalar We = rho*magSqr(Un)*d/sigma;
// Critical Weber number
const scalar Wec = Adry_*pow(La, -0.183);
Henry Weller
committed
if (We < Wec) // Adhesion - assume absorb
absorbInteraction(p, pp, facei, m, keepParticle);
Henry Weller
committed
else // Splash
Henry Weller
committed
// Ratio of incident mass to splashing mass
const scalar mRatio = 0.2 + 0.6*rndGen_.sample01<scalar>();
splashInteraction
(p, pp, facei, mRatio, We, Wec, sigma, keepParticle);
}
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::wetSplashInteraction
(
//regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
parcelType& p,
const polyPatch& pp,
bool& keepParticle
)
{
if (debug)
{
Info<< "Parcel " << p.origId() << " wetSplashInteraction" << endl;
}
const liquidProperties& liq = thermo_.liquids().properties()[0];
// Patch face velocity and normal
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
const vector& nf = pp.faceNormals()[facei];
Henry Weller
committed
// Local pressure
const scalar pc = thermo_.thermo().p()[p.cell()];
// Retrieve parcel properties
const scalar m = p.mass()*p.nParticle();
const scalar rho = p.rho();
const scalar d = p.d();
vector& U = p.U();
const scalar sigma = liq.sigma(pc, p.T());
const scalar mu = liq.mu(pc, p.T());
const vector Urel = p.U() - Up;
const vector Un = nf*(Urel & nf);
const vector Ut = Urel - Un;
// Laplace number
const scalar La = rho*sigma*d/sqr(mu);
// Weber number
const scalar We = rho*magSqr(Un)*d/sigma;
// Critical Weber number
const scalar Wec = Awet_*pow(La, -0.183);
Henry Weller
committed
if (We < 2) // Adhesion - assume absorb
absorbInteraction(p, pp, facei, m, keepParticle);
Henry Weller
committed
else if ((We >= 2) && (We < 20)) // Bounce
Henry Weller
committed
// Incident angle of impingement
const scalar theta = piByTwo - acos(U/mag(U) & nf);
Henry Weller
committed
// Restitution coefficient
const scalar epsilon = 0.993 - theta*(1.76 - theta*(1.56 - theta*0.49));
Henry Weller
committed
// Update parcel velocity
U = -epsilon*(Un) + 5.0/7.0*(Ut);
Henry Weller
committed
else if ((We >= 20) && (We < Wec)) // Spread - assume absorb
absorbInteraction(p, pp, facei, m, keepParticle);
Henry Weller
committed
else // Splash
Henry Weller
committed
// Ratio of incident mass to splashing mass
// splash mass can be > incident mass due to film entrainment
const scalar mRatio = 0.2 + 0.9*rndGen_.sample01<scalar>();
splashInteraction
(p, pp, facei, mRatio, We, Wec, sigma, keepParticle);
}
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::splashInteraction
(
//regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel,
const parcelType& p,
const polyPatch& pp,
const scalar mRatio,
const scalar We,
const scalar Wec,
const scalar sigma,
bool& keepParticle
)
{
// Patch face velocity and normal
const fvMesh& mesh = this->owner().mesh();
const vector& Up = this->owner().U().boundaryField()[pp.index()][facei];
const vector& nf = pp.faceNormals()[facei];
// Determine direction vectors tangential to patch normal
const vector tanVec1 = tangentVector(nf);
const vector tanVec2 = nf^tanVec1;
// Retrieve parcel properties
const scalar np = p.nParticle();
const scalar m = p.mass()*np;
const scalar d = p.d();
const vector Urel = p.U() - Up;
const vector Un = nf*(Urel & nf);
const vector Ut = Urel - Un;
const vector& posC = mesh.C()[p.cell()];
const vector& posCf = mesh.Cf().boundaryField()[pp.index()][facei];
Henry Weller
committed
// Total mass of (all) splashed parcels
const scalar mSplash = m*mRatio;
Henry Weller
committed
// Number of splashed particles per incoming particle
const scalar Ns = 5.0*(We/Wec - 1.0);
Henry Weller
committed
// Average diameter of splashed particles
const scalar dBarSplash = 1/cbrt(6.0)*cbrt(mRatio/Ns)*d + ROOTVSMALL;
Henry Weller
committed
// Cumulative diameter splash distribution
const scalar dMax = 0.9*cbrt(mRatio)*d;
const scalar dMin = 0.1*dMax;
const scalar K = exp(-dMin/dBarSplash) - exp(-dMax/dBarSplash);
Henry Weller
committed
// Surface energy of secondary parcels [J]
scalar ESigmaSec = 0;
Henry Weller
committed
// Sample splash distribution to determine secondary parcel diameters
scalarList dNew(parcelsPerSplash_);
scalarList npNew(parcelsPerSplash_);
forAll(dNew, i)
{
const scalar y = rndGen_.sample01<scalar>();
dNew[i] = -dBarSplash*log(exp(-dMin/dBarSplash) - y*K);
npNew[i] = mRatio*np*pow3(d)/pow3(dNew[i])/parcelsPerSplash_;
ESigmaSec += npNew[i]*sigma*p.areaS(dNew[i]);
Henry Weller
committed
// Incident kinetic energy [J]
const scalar EKIn = 0.5*m*magSqr(Un);
Henry Weller
committed
// Incident surface energy [J]
const scalar ESigmaIn = np*sigma*p.areaS(d);
Henry Weller
committed
// Dissipative energy
const scalar Ed = max(0.8*EKIn, np*Wec/12*pi*sigma*sqr(d));
Henry Weller
committed
// Total energy [J]
const scalar EKs = EKIn + ESigmaIn - ESigmaSec - Ed;
Henry Weller
committed
// Switch to absorb if insufficient energy for splash
absorbInteraction(p, pp, facei, m, keepParticle);
Henry Weller
committed
// Helper variables to calculate magUns0
const scalar logD = log(d);
const scalar coeff2 = log(dNew[0]) - logD + ROOTVSMALL;
scalar coeff1 = 0.0;
forAll(dNew, i)
{
coeff1 += sqr(log(dNew[i]) - logD);
}
Henry Weller
committed
// Magnitude of the normal velocity of the first splashed parcel
sqrt(2.0*parcelsPerSplash_*EKs/mSplash/(1.0 + coeff1/sqr(coeff2)));
// Set splashed parcel properties
forAll(dNew, i)
{
const vector dirVec = splashDirection(tanVec1, tanVec2, -nf);
// Create a new parcel by copying source parcel
parcelType* pPtr = new parcelType(p);
pPtr->origId() = pPtr->getNewParticleID();
pPtr->origProc() = Pstream::myProcNo();
if (splashParcelType_ >= 0)
{
pPtr->typeId() = splashParcelType_;
}
Henry Weller
committed
// Perturb new parcels towards the owner cell centre
pPtr->track(0.5*rndGen_.sample01<scalar>()*(posC - posCf), 0);
pPtr->nParticle() = npNew[i];
pPtr->d() = dNew[i];
pPtr->U() = dirVec*(mag(Cf_*Ut) + magUns0*(log(dNew[i]) - logD)/coeff2);
// Apply correction to velocity for 2-D cases
meshTools::constrainDirection(mesh, mesh.solutionD(), pPtr->U());
// Add the new parcel
this->owner().addParticle(pPtr);
nParcelsSplashed_++;
}
Henry Weller
committed
// Transfer remaining part of parcel to film 0 - splashMass can be -ve
// if entraining from the film
const scalar mDash = m - mSplash;
absorbInteraction(p, pp, facei, mDash, keepParticle);
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class CloudType>
Foam::ThermoSurfaceFilm<CloudType>::ThermoSurfaceFilm
(
const dictionary& dict,
SurfaceFilmModel<CloudType>(dict, owner, typeName),
thermo_
(
owner.db().objectRegistry::template lookupObject<SLGThermo>("SLGThermo")
),
TFilmPatch_(0),
CpFilmPatch_(0),
interactionType_
(
interactionTypeEnum(this->coeffDict().getWord("interactionType"))
),
deltaWet_(0.0),
splashParcelType_(0),
parcelsPerSplash_(0),
Adry_(0.0),
Awet_(0.0),
Cf_(0.0),
nParcelsSplashed_(0)
{
Info<< " Applying " << interactionTypeStr(interactionType_)
<< " interaction model" << endl;
if (interactionType_ == itSplashBai)
{
this->coeffDict().readEntry("deltaWet", deltaWet_);
this->coeffDict().getOrDefault("splashParcelType", -1);
this->coeffDict().getOrDefault("parcelsPerSplash", 2);
this->coeffDict().readEntry("Adry", Adry_);
this->coeffDict().readEntry("Awet", Awet_);
this->coeffDict().readEntry("Cf", Cf_);
filmModel_ =
const_cast<regionModels::surfaceFilmModels::surfaceFilmRegionModel*>
(
this->owner().mesh().time().objectRegistry::template findObject
<
regionModels::surfaceFilmModels::surfaceFilmRegionModel
>
(
"surfaceFilmProperties"
)
);
template<class CloudType>
Foam::ThermoSurfaceFilm<CloudType>::ThermoSurfaceFilm
(
const ThermoSurfaceFilm<CloudType>& sfm
)
:
SurfaceFilmModel<CloudType>(sfm),
rndGen_(sfm.rndGen_),
thermo_(sfm.thermo_),
TFilmPatch_(sfm.TFilmPatch_),
CpFilmPatch_(sfm.CpFilmPatch_),
interactionType_(sfm.interactionType_),
deltaWet_(sfm.deltaWet_),
splashParcelType_(sfm.splashParcelType_),
parcelsPerSplash_(sfm.parcelsPerSplash_),
Adry_(sfm.Adry_),
Awet_(sfm.Awet_),
Cf_(sfm.Cf_),
nParcelsSplashed_(sfm.nParcelsSplashed_)
{
filmModel_ =
const_cast<regionModels::surfaceFilmModels::surfaceFilmRegionModel*>
(
this->owner().mesh().time().objectRegistry::template findObject
<
regionModels::surfaceFilmModels::surfaceFilmRegionModel
>
(
"surfaceFilmProperties"
)
);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
template<class CloudType>
Foam::ThermoSurfaceFilm<CloudType>::~ThermoSurfaceFilm()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class CloudType>
bool Foam::ThermoSurfaceFilm<CloudType>::transferParcel
(
parcelType& p,
const polyPatch& pp,
bool& keepParticle
const fvMesh& mesh = this->owner().mesh();
const label patchi = pp.index();
// Check the singleLayer film models
if (filmModel_ && filmModel_->isRegionPatch(patchi))
const label facei = pp.whichFace(p.face());
switch (interactionType_)
bounceInteraction(p, pp, facei, keepParticle);
break;
}
case itAbsorb:
{
const scalar m = p.nParticle()*p.mass();
absorbInteraction(p, pp, facei, m, keepParticle);
break;
}
case itSplashBai:
{
bool dry = this->deltaFilmPatch_[patchi][facei] < deltaWet_;
drySplashInteraction(p, pp, facei, keepParticle);
wetSplashInteraction( p, pp, facei, keepParticle);
}
break;
}
default:
{
FatalErrorInFunction
<< "Unknown interaction type enumeration"
<< abort(FatalError);
}
Henry Weller
committed
// Transfer parcel/parcel interactions complete
return true;
}
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
else
{
// Check the finite area films
wordList names =
mesh.time().objectRegistry::template
sortedNames<regionModels::regionFaModel>();
forAll(names, i)
{
const regionModels::regionFaModel* regionFa =
mesh.time().objectRegistry::template findObject
<
regionModels::regionFaModel
>(names[i]);
// Check that regionFa is a areaFilm
if (regionFa && isA<areaFilm>(*regionFa))
{
areaFilm& film =
const_cast<areaFilm&>(refCast<const areaFilm>(*regionFa));
const label facei = pp.whichFace(p.face());
switch (interactionType_)
{
// It only supports absorp model
case itAbsorb:
{
const scalar m = p.nParticle()*p.mass();
absorbInteraction
(
film, p, pp, facei, m, keepParticle
);
break;
}
default:
{
FatalErrorInFunction
<< "Unknown interaction type enumeration"
<< abort(FatalError);
}
}
// Transfer parcel/parcel interactions complete
return true;
}
}
}
Henry Weller
committed
// Parcel not interacting with film
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::cacheFilmFields
(
const label filmPatchi,
const label primaryPatchi,
const regionModels::surfaceFilmModels::surfaceFilmRegionModel& filmModel
)
{
SurfaceFilmModel<CloudType>::cacheFilmFields
(
filmPatchi,
primaryPatchi,
filmModel
);
TFilmPatch_ = filmModel.Ts().boundaryField()[filmPatchi];
filmModel.toPrimary(filmPatchi, TFilmPatch_);
CpFilmPatch_ = filmModel.Cp().boundaryField()[filmPatchi];
filmModel.toPrimary(filmPatchi, CpFilmPatch_);
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::cacheFilmFields
(
const label filmPatchi,
const areaFilm& filmModel
)
{
SurfaceFilmModel<CloudType>::cacheFilmFields
(
filmPatchi,
filmModel
);
const volSurfaceMapping& map = filmModel.region().vsm();
TFilmPatch_.setSize(filmModel.Tf().size(), Zero);
map.mapToField(filmModel.Tf(), TFilmPatch_);
//map.mapToField(filmModel.Cp(), CpFilmPatch_);
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::setParcelProperties
(
parcelType& p,
const label filmFacei
SurfaceFilmModel<CloudType>::setParcelProperties(p, filmFacei);
// Set parcel properties
p.T() = TFilmPatch_[filmFacei];
p.Cp() = CpFilmPatch_[filmFacei];
}
template<class CloudType>
void Foam::ThermoSurfaceFilm<CloudType>::info(Ostream& os)
SurfaceFilmModel<CloudType>::info(os);
label nSplash0 = this->template getModelProperty<label>("nParcelsSplashed");
label nSplashTotal =
nSplash0 + returnReduce(nParcelsSplashed_, sumOp<label>());
os << " - new splash parcels = " << nSplashTotal << endl;
Henry Weller
committed
if (this->writeTime())
{
this->setModelProperty("nParcelsSplashed", nSplashTotal);
nParcelsSplashed_ = 0;
}